Add variable of superclass to dictionary subclass and read value - python-3.x

I'm having trouble accessing a superclass variable from a dictionary in the subclass.
The following code is a simplified example:
class SetStuff:
def __init__(self):
self.temperature = 0.0
def set_temp(self, temp):
self.temperature = temp
class DoStuff(SetStuff):
def __init__(self):
super().__init__()
self.info_dict = {"temp": {"current_temp": self.temperature}}
def print_stuff(self):
print("temp_var:", self.temperature)
print("dict:", self.info_dict)
test_stuff = DoStuff()
test_stuff.set_temp(12.1)
test_stuff.print_stuff()
The result of the final call is:
temp_var: 12.1
dict: {'temp': {'current_temp': 0.0}}
Whereas I expected the printed dictionary to contain 12.1. I can't seem to fathom what happens here and how I can fix this.
Any help would be greatly appreciated.

Look at where self.info_dict is set. It's in the __init__ so the value of self.temperature is indeed zero for current_temp because it's being set to the initial value of self.temperature

Related

Specify class variable in Python to be a numpy array of not yet known size

I have a class like
class MyClass:
def __init__(self):
self.will_be_a_numpy_array = None
def compute():
tmp = receive_data()
self.will_be_a_numpy_array = np.zeros(len(tmp))
# process each item in tmp, save result in corresponding element of self.will_be_a_numpy_array
Here __init__ method is vague regarding the type of self.will_be_a_numpy_array variable. It is unclear to fellow developer or compiler what type of variable should be expected. I cannot initialize variable with self.will_be_a_numpy_array = np.zeros(len(tmp)) because I haven't received data yet. What is the right way to articulate variable type in this case?
You can use the strategy that scikit-learn uses for their estimators, namely, you create the attribute when you receive the data and you use a trailing underscore to warn that this is an attribute that is not created at initialisation:
class MyClass:
def __init__(self):
pass
def process(self, data):
self.data_ = np.array(data)
def is_processed(self):
return hasattr(self, 'data_')

Multiple inheritance problem with super()

I'm having a problem with multiple inheritance that I can't seem to figure out. Here is a very abstracted minimal example that reproduces my error (my code is much more complex than this).
class Thing(object):
def __init__(self, x=None):
self.x = x
class Mixin(object):
def __init__(self):
self.numbers = [1,2,3]
def children(self):
return [super().__init__(x=num) for num in self.numbers]
class CompositeThing(Mixin, Thing):
def __init__(self):
super().__init__()
def test(self):
for child in self.children():
print(child.x)
obj = CompositeThing()
obj.test()
Per this, I expect the children() method to return a list of Things built up from self.numbers. Instead, I get TypeError: super(type, obj): obj must be an instance or subtype of type. Incidentally, the same thing happens if I don't call the constructor and allow children to return super() 3 times (i.e., the uninstantiated superclass). Any ideas why this might be happening?
Thanks in advance!
In line 9 of your code, it looks like you are trying to call __init__ of object. I am assuming you meant to have Mixin inherit from Thing.
class Thing(object):
def __init__(self, x=None):
self.x = x
class Mixin(Thing):
def __init__(self):
self.numbers = [1,2,3]
def children(self):
return [super().__init__(x=num) for num in self.numbers] # Now calls Thing.__init__ instead of object.__init__
class CompositeThing(Mixin, Thing):
def __init__(self):
super().__init__()
def test(self):
for child in self.children():
print(child.x)
obj = CompositeThing()
obj.test()
Actually, I figured it out. There were two problems: (1) super() doesn't work as expected inside comprehensions because comprehensions in Py3 have their own scope - this was causing the TypeError I was experiencing. (2) What I was really trying to do was create a new instance of the parent, rather than calling a method from the parent. I have posted a new question for just the latter problem for clarity.

Could you fix the Python init function?

I have this Python code...
class clss:
def __init__(self, d):
self.data = d
But every time I run it, I get this error...
AttributeError: 'clss' object has no attribute 'data'
How do I fix the error?
Your variable data is not defined anywhere, hence it cannot be assigned to self.data.
You'll want to pass data as an argument to __init__ instead of d:
class clss:
def __init__(self, data):
self.data = data

Python overriding default attribute assignment

for a specific framework i work with, i need to define object attributes as special classes, for example, instead of writing this:
class A:
def __init__(self):
self.some_int = 2
i would need to write:
class A:
def __init__(self):
self.some_int = SpecialIntWrapper(name = "some_int", value = 2)
I would like to somehow override operators/methods so that typing the first code (self.some_int = 2) will call SpecialIntWrapper behind the scenes, with the attribute name and value.
is this possible?
Basically there are two ways - via a #property decorator (preferable unless you want to affect arbitrary names)
class MyClass:
def __init__(self):
self.some_int = 2
# if you know the name of the property define it as a property - a getter
#property
def some_int(self):
return self._some_int
# and a setter
#some_int.setter
def some_int(self, value):
self._some_int = SpecialIntWrapper("some_int", value)
or overloading the __setattr__ magic method
class MyClass:
def __init__(self):
self.some_int = 2
def __setattr__(self, name, value):
# in general if you dont know the names of the properties
# beforehand you can somehow filter them here
if name == "some_int":
super().__setattr__(name, SpecialIntWrapper(name=name, value=value))
else:
# to use the setattr in a default way, just call it via super(Python 3)
super().__setattr__(name, value)
either way the some_int will be initialized to the SpecialIntWrapper instance
>>>print(MyClass().some_int)
<__main__.SpecialIntWrapper object at 0x03721810>
Something like this
class SpecialIntWrapper:
def __init__(self, name, value):
pass
class MyClass:
def __init__(self):
self.some_int = 3
def __setattr__(self, key, value):
if key == 'some_int':
self.__dict__[key] = SpecialIntWrapper(key, value)
else:
self.__dict__[key] = value
print(MyClass().some_int)
# >>> <__main__.SpecialIntWrapper object at 0x1076f1748>

dynamic class inheritance using super

I'm trying to dynamically create a class using type() and assign an __init__ constructor which calls super().__init__(...); however, when super() gets called I receive the following error:
TypeError: super(type, obj): obj must be an instance or subtype of type
Here is my code:
class Item():
def __init__(self, name, description, cost, **kwargs):
self.name = name
self.description = description
self.cost = cost
self.kwargs = kwargs
class ItemBase(Item):
def __init__(self, name, description, cost):
super().__init__(name, description, cost)
def __constructor__(self, n, d, c):
super().__init__(name=n, description=d, cost=c)
item = type('Item1', (ItemBase,), {'__init__':__constructor__})
item_instance = item('MyName', 'MyDescription', 'MyCost')
Why is super() inside the __constructor__ method not understanding the object parameter; and how do I fix it?
Solution 1: Using cls = type('ClassName', ...)
Note the solution of sadmicrowave creates an infinite loop if the dynamically-created class gets inherited as self.__class__ will correspond to the child class.
An alternative way which do not have this issue is to assigns __init__ after creating the class, such as the class can be linked explicitly through closure. Example:
# Base class
class A():
def __init__(self):
print('A')
# Dynamically created class
B = type('B', (A,), {})
def __init__(self):
print('B')
super(B, self).__init__()
B.__init__ = __init__
# Child class
class C(B):
def __init__(self):
print('C')
super().__init__()
C() # print C, B, A
Solution 2: Using MyClass.__name__ = 'ClassName'
An alternative way to dynamically create class is to define a class inside the function, then reassign the __name__ and __qualname__ attributes:
class A:
def __init__(self):
print(A.__name__)
def make_class(name, base):
class Child(base):
def __init__(self):
print(Child.__name__)
super().__init__()
Child.__name__ = name
Child.__qualname__ = name
return Child
B = make_class('B', A)
class C(B):
def __init__(self):
print(C.__name__)
super().__init__()
C() # Display C B A
Here is how I solved the issue. I reference the type() method to dynamically instantiate a class with variable references as such:
def __constructor__(self, n, d, c, h):
# initialize super of class type
super(self.__class__, self).__init__(name=n, description=d, cost=c, hp=h)
# create the object class dynamically, utilizing __constructor__ for __init__ method
item = type(item_name, (eval("{}.{}".format(name,row[1].value)),), {'__init__':__constructor__})
# add new object to the global _objects object to be used throughout the world
self._objects[ item_name ] = item(row[0].value, row[2].value, row[3].value, row[4].value)
There may be a better way to accomplish this, but I needed a fix and this is what I came up with... use it if you can.

Resources