Python class inheritance: AttributeError: '[SubClass]' object has no attribute 'xxx' - python

I have the following base class and subclass:
class Event:
def __init__(self, sr1=None, foobar=None):
self.sr1 = sr1
self.foobar = foobar
self.state = STATE_NON_EVENT
# Event class wrappers to provide syntatic sugar
class TypeTwoEvent(Event):
def __init__(self, level=None):
self.sr1 = level
self.state = STATE_EVENT_TWO
Further on in my code, I am inspecting an instance of a TypeTwoEvent class, checking for a field I know exists in the base class - I expected it to be defaulted to value None. However, my code raises the following exception:
AttributeError: 'TypeTwoEvent' object has no attribute 'foobar'
I was under the impression that the base class fields would be inherited by the subclass and that creating an instance of a subclass will instantiate the base class (and thus invoke its constructor) ...
What am I missing here? Why does TypeTwoEvent not have a foobar attribute - when the base class from which it is derived has a foobar attribute?

Your subclass should be:
class TypeTwoEvent(Event):
def __init__(self, level=None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.sr1 = level
self.state = STATE_EVENT_TWO
Because you override the __init__ method, so you need to call the parent method if you want the parent behavior to happen.
Remember, __init__ is not a special method dispite its strange name. It's just the method automatically called after the object is created. Otherwise it's an ordinary method, and ordinary inheritance rules apply.
super().__init__(arguments, that, goes, to, parents)
is the syntax to call the parent version of the method.
For *args and **kwargs, it just ensures we catch all additional arguments passed to __init__ and pass it to the parent method, as you child method signature didn't do it and the parent need these arguments to work.

You're overriding the constructor (__init__) of the parent class. To extend it, you need to explicitly call the constructor of the parent with a super() call.
class TypeTwoEvent(Event):
def __init__(self, level=None, **kwargs):
# the super call to set the attributes in the parent class
super().__init__(**kwargs)
# now, extend other attributes
self.sr1 = level
self.state = STATE_EVENT_TWO
Note that the super call is not always at the top of the __init__ method in your sub-class. Its location depends on your situation and logic.

When the instance is created, its __init__ method is called. In this case, that is TypeTwoEvent.__init__. Superclass methods will not be called automatically because that would be immensely confusing.
You should call Event.__init__(self, ...) from TypeTwoEvent.__init__ (or use super, but if you're not familiar with it, read up on it first so you know what you're doing).

You need to call the __init__ method of the base class from the __init__ method of the inherited class.
See here for how to do this.

I've had the same problem, but in my case I put super().__init__() on the bottom of my derived class and that's why it doesn't work. Because I tried to use attributes that are not initialized.

Related

How to reflect Base's class __init__ parameters (with docs string / type hints etc) through Child Class __Init__

Suppose I have a base class that has many arguments in the __init__ method. Child class basically starts on top of the base class. Now, I want the child class (at instantiation) to show the docstring (like args, type hints etc) of the base class. I do not want to copy the base class's __init__ arguments manually into the child class __init__ method
So if I have the following code:
# base class
class BaseClass:
def __init__(self, base_arg1:str,base_arg2:str):
"this is our base class"
self.base_arg1 = base_arg1
self.base_arg2 = base_arg2
print("running base")
# child
class ChildClass(BaseClass):
def __init__(self,a:str,b:str):
"this is our child class"
super().__init__(a,b)
return None
In this case, I will first need to write arguments in the child class __init__ method. Then I have to write them again in the super() function. Plus when I am constructing the child class's __init__ method, I need to repeat the docstring/type hinting. Plus, let's say, if later on, another argument gets added to the base class, I will need to copy over the argument (and its docstring/ type hint) to the child class again.
Is there a more efficient way of calling the base class, so that when I call the Child class, type hints and docstring automatically show/pick the Base Classes' argument and docstring? I really want to avoid the double documentation and type hinting for Base & Child Class, whereas arguments both Classes take to instantiate are exactly the same.
If you are not changing any arguments in child class and they are the same as the parent class then there is no need to call __init__() in child class. When you crate a child class it takes all arguments from parent class. If you want some arguments from parent class and you want to add more arguments then you need to use super().__init__(parent_arg1, parent_arg2). And when you create an object of your child class, it also has all methods from both child and parent class. Btw in your child class __init__() you can't put str: arg it must be arg: str.
Based on the comment of #jfaccioni, I resolved it by not implementing init inside the child class. Now when I instantiate the child class, it behaves like as if I am instantiating Base class.
# base class
class BaseClass:
def __init__(self, base_arg1:str,base_arg2:str):
"this is our base class"
self.base_arg1 = base_arg1
self.base_arg2 = base_arg2
print("running base")
# child
class ChildClass(BaseClass):
def some_other_fun(arg):
self.arg = arg
return None

Why is Python super used in the child's init method?

According to Python docs super()
is useful for accessing inherited methods that have been overridden in
a class.
I understand that super refers to the parent class and it lets you access parent methods. My question is why do people always use super inside the init method of the child class? I have seen it everywhere. For example:
class Person:
def __init__(self, name):
self.name = name
class Employee(Person):
def __init__(self, **kwargs):
super().__init__(name=kwargs['name']) # Here super is being used
def first_letter(self):
return self.name[0]
e = Employee(name="John")
print(e.first_letter())
I can accomplish the same without super and without even an init method:
class Person:
def __init__(self, name):
self.name = name
class Employee(Person):
def first_letter(self):
return self.name[0]
e = Employee(name="John")
print(e.first_letter())
Are there drawbacks with the latter code? It looks so much cleanr to me. I don't even have to use the boilerplate **kwargs and kwargs['argument'] syntax.
I am using Python 3.8.
Edit: Here's another stackoverflow questions which has code from different people who are using super in the child's init method. I don't understand why. My best guess is there's something new in Python 3.8.
The child might want to do something different or more likely additional to what the super class does - in this case the child must have an __init__.
Calling super’s init means that you don’t have to copy/paste (with all the implications for maintenance) that init in the child’s class, which otherwise would be needed if you wanted some additional code in the child init.
But note there are complications about using super’s init if you use multiple inheritance (e.g. which super gets called) and this needs care. Personally I avoid multiple inheritance and keep inheritance to aminimum anyway - it’s easy to get tempted into creating multiple levels of inheritance/class hierarchy but my experience is that a ‘keep it simple’ approach is usually much better.
The potential drawback to the latter code is that there is no __init__ method within the Employee class. Since there is none, the __init__ method of the parent class is called. However, as soon as an __init__ method is added to the Employee class (maybe there's some Employee-specific attribute that needs to be initialized, like an id_number) then the __init__ method of the parent class is overridden and not called (unless super.__init__() is called) and then an Employee will not have a name attribute.
The correct way to use super here is for both methods to use super. You cannot assume that Person is the last (or at least, next-to-last, before object) class in the MRO.
class Person:
def __init__(self, name, **kwargs):
super().__init__(**kwargs)
self.name = name
class Employee(Person):
# Optional, since Employee.__init__ does nothing
# except pass the exact same arguments "upstream"
def __init__(self, **kwargs):
super().__init__(**kwargs)
def first_letter(self):
return self.name[0]
Consider a class definition like
class Bar:
...
class Foo(Person, Bar):
...
The MRO for Foo looks like [Foo, Person, Bar, object]; the call to super().__init__ inside Person.__init__ would call Bar.__init__, not object.__init__, and Person has no way of knowing if values in **kwargs are meant for Bar, so it must pass them on.

Python parent class init call child's overwritten method

Please take a look at the sample code below. The parent class, needs to call the setup() function, but that function needs to be defined/overwritten in the child class. But the sample code below shows that when child is initialized and called super(), the super's method calls the parent setup() which contains nothing.
Intended behavior is when child is instantiated, it calls parent's __init__ by using super() and automatically call the setup function defined in the child. Setting Human.setup() as abstract class also did not seem to work.
Google can only find information on how to call parent's method from child, but this case is the reverse, "How to call child's method from parent". Or is it not possible in python3?
The reason it needs to call the setup function of the child is due to each child class will be having a different setup procedure. But they share most of the methods defined in the parent class.
class Human(object):
def __init__(self):
self.setup()
def setup(self):
pass
class child(Human):
def __init__(self):
super()
def setup(self):
self.name = "test"
>>> A = child()
>>> A.name
AttributeError: 'child' object has no attribute 'name'
You are not calling Human's __init__ from child's __init__.
super() gives you a handle through which to call superclass methods, but you need to do that via method calls on super().
In this case:
class Child(Human):
def __init__(self):
super().__init__()

Python: Why do I get an exception using super() but not with explicit super class name?

I am getting an exception when I try to access a base class's property using super(), but not when I use the base class name explicitly. Here is the derived class:
from CPSA_TransactionLogOutSet import CPSA_TransactionLogOutSet
class CPSA_TransactionFailureSet(CPSA_TransactionLogOutSet):
def __init__(self, connection, failedTransactionKey):
super().__init__(connection)
CPSA_TransactionLogOutSet.C_TRANS_TYP = "TRANS_FAIL"
super().C_TRANS_TYP = "TRANS_FAIL"
super().DefaultTableName = 'CPSA_TRANSACTION_LOG_IN'
super()._keyFields.append('J_TRANS_SEQ')
but trying to create an instance raises an AttributeError exception:
AttributeError: 'super' object has no attribute 'C_TRANS_TYP'
The base class consists of an __init__() method and a set of properties, only one of which is shown here:
class CPSA_TransactionLogOutSet(Recordset):
def __init__(self, connection):
super().__init__(connection)
self.DefaultTableName = 'CPSA_TRANSACTION_LOG_OUT'
#property
def C_TRANS_TYP(self):
return self.GetValue('C_TRANS_TYP')
#C_TRANS_TYP.setter
def C_TRANS_TYP(self, value):
self.SetValue('C_TRANS_TYP', value)
Why can't I use super() to access the C_TRANS_TYP property?
You don't need to use super() at all because there is no override on the current class. The descriptor will be bound to self without super(). The same applies to the other attributes on self:
def __init__(self, connection, failedTransactionKey):
super().__init__(connection)
self.C_TRANS_TYP = "TRANS_FAIL"
self.DefaultTableName = 'CPSA_TRANSACTION_LOG_IN'
self._keyFields.append('J_TRANS_SEQ')
super() is only needed to access descriptors that would not otherwise be reachable via self. The normal access path (via the instance) suffices here.
super() can't be used to bind data descriptors in an assignment or del obj.attr statement, because super() objects do not implement __set__ or __delete__. In other words, using super().attribute works for reading the attribute only, never for writing or deleting.
Setting CPSA_TransactionLogOutSet.C_TRANS_TYP is also incorrect; that replaces the descriptor object on the class. By executing that line, you removed the descriptor from the class hierarchy altogether, so neither self.C_TRANS_TYP nor super().C_TRANS_TYP would trigger the property you defined before.

Python multiple inheritance: which __new__ to call?

I have a class Parent. I want to define a __new__ for Parent so it does some magic upon instantiation (for why, see footnote). I also want children classes to inherit from this and other classes to get Parent's features. The Parent's __new__ would return an instance of a subclass of the child class's bases and the Parent class.
This is how the child class would be defined:
class Child(Parent, list):
pass
But now I don't know what __new__ to call in Parent's __new__. If I call object.__new__, the above Child example complains that list.__new__ should be called. But how would Parent know that? I made it work so it loops through all the __bases__, and call each __new__ inside a try: block:
class Parent(object):
def __new__(cls, *args, **kwargs):
# There is a special wrapper function for instantiating instances of children
# classes that passes in a 'bases' argument, which is the __bases__ of the
# Children class.
bases = kwargs.get('bases')
if bases:
cls = type('name', bases + (cls,), kwargs.get('attr', {}))
for base in cls.__mro__:
if base not in (cls, MyMainType):
try:
obj = base.__new__(cls)
break
except TypeError:
pass
return obj
return object.__new__(cls)
But this just looks like a hack. Surely, there must be a better way of doing this?
Thanks.
The reason I want to use __new__ is so I can return an object of a subclass that has some dynamic attributes (the magic __int__ attributes, etc) assigned to the class. I could have done this in __init__, but I would not be able to modify self.__class__ in __init__ if the new class has a different internal structure, which is the case here due to multiple inheritance.
I think this will get you what you want:
return super(Parent, cls).__new__(cls, *args, **kwargs)
and you won't need the bases keyword argument. Unless I'm getting you wrong and you're putting that in there on purpose.

Categories