I am building a plotting class in Python, and am hoping to do the following. I want a graphics window using PyQt5 that also inherits from some custom classes I have made (such as a curve fitting class). In order for the curve fitting class to manipulate data that persists in the plotting class, it must have a reference to the data that is contained in the plotting class. Because of this, I have chosen the plotting class to inherit from the CurveFitting class.
The problem seems to arise in inheriting both from PyQt5's GraphicsWindow class and my custom class, which accept different numbers of arguments. I have read that Python does not play nice with classes that inherit different numbers of arguments using the "super" functionality, so I decided to make my custom CurveFitting class accept **kwargs, which would then give it a reference to the parent. However, I then encountered a different error which I do not understand. Below is a tidied up example of what I'm trying to do
import numpy as np
from pyqtgraph import GraphicsWindow
class ClassA():
def __init__(self, **kwargs):
super().__init__()
self.kwargs = kwargs
self.parent = self.kwargs['parent']
self.xdata = self.parent.xdata
def print_data(self):
print(self.parent.xdata)
print(self.parent.ydata)
class classC(GraphicsWindow, ClassA):
def __init__(self):
kwargs = {}
kwargs['parent'] = self
kargs = kwargs
self.xdata = np.linspace(0, 100, 101)
self.ydata = np.linspace(0, 200, 101)
super().__init__(**kwargs)
# ClassA.__init__(self, **kwargs)
# GraphicsWindow.__init__(self)
instC = classC()
instC.print_data()
When I run the above I get "RuntimeError: super-class init() of type classC was never called" on the "super().__init(**kwargs)" line, which I honestly do not understand at all, and have tried googling for a while but to no avail.
Additionally, I have tried commenting out the line, and uncommenting the next two lines to inherit from each class manually, but this also does not work. What I find pretty weird is that if I comment one of those two lines out, they both work individually, but together they do not work. For example, if I run it with both lines, it gives me an error that kwargs has no key word 'parent', as if it didn't even pass **kwargs.
Is there a way to inherit from two classes that take a different number of initialization parameters like this? Is there a totally different way I could be approaching this problem? Thanks.
The immediate problem with your code is that ClassC inherits from GraphicsWindow as its first base class, and ClassA is the second base class. When you call super, only one gets called (GraphicsWindow) and if it was not designed to work with multiple inheritance (as seems to be the case), it may not call super itself or may not pass on the arguments that ClassA expects.
Just switching the order of the base classes may be enough to make it work. Python guarantees that the base classes will be called in the same relative order that they appear in the class statement in (though other classes may be inserted between them in the MRO if more inheritance happens later). Since ClassA.__init__ does call super, it should work better!
It can be tricky to make __init__ methods work with multiple inheritance though, even if all the classes involved are designed to work with it. This is why positional arguments are often avoided, since their order can become very confusing (since child classes can only add positional arguments ahead of their parent's positional arguments unless they want to repeat all the names). Using keyword arguments is definitely a better approach.
But the code you have is making dealing with keyword arguments a bit more complicated than it should be. You shouldn't need to explicitly create dictionaries to pass on with **kwargs syntax, nor should you need to extract keyword values from a a dict you accepted with a **kwargs argument. Usually each function should name the arguments it takes, and only use **kwargs for unknown arguments (that may be needed by some other class in the MRO). Here's what that looks like:
class Base1:
def __init__(self, *, arg1, arg2, arg3, **kwargs): # the * means the other args are kw-only
super().__init__(**kwargs) # always pass on all unknown arguments
... # use the named args here (not kwargs)
class Base2:
def __init__(self, *, arg4, arg5, arg6, **kwargs):
super().__init__(**kwargs)
...
class Derived(Base1, Base2):
def __init__(self, *, arg2, arg7, **kwargs): # child can accept args used by parents
super().__init__(arg2=arg2+1, arg6=3, **kwargs) # it can then modify or create from
... # scratch args to pass to its parents
obj = Derived(arg1=1, arg2=2, arg3=3, arg4=4, arg5=5, arg7=7) # note, we've skipped arg6
# and Base1 will get 3 for arg2
But I'd also give serious though to whether inheritance makes any sense in your situation. It may make more sense for one of your two base classes to be encapsulated within your child class, rather than being inherited from. That is, you'd inherit from only one of ClassA or GraphicsWindow, and store an instance of the other in each instance of ClassC. (You could even inherit from neither base class, and encapsulate them both.) Encapsulation is often a lot easier to reason about and get right than inheritance.
Related
Preamble
This is a rather basic question, I realize, but I haven't been able to find a sturdy reference for it, which would likely be a mixture of technical details and best practices for well-behaved classes.
Question
When a parent and child class both define the same initialization parameter, but with default values, what's the best way to get sane behavior when the child class is created?
My assumptions are:
Classes only accept named parameters, I don't need to deal with positional arguments. That simplifies many things, both theoretically in reasoning about situations and practically in taking arguments from external config files, etc.
__init__ methods may be more sophisticated than just setting self.foo = foo for their arguments - they may transform it before storing, use it to set other params, etc. and I'd like to be as respectful of that as possible.
Subclasses never break the interfaces of their parents, both for __init__ parameters and for attributes. Having a different default value is not considered "breaking".
Classes should never have to be aware of their subclasses, they should just do things in "reasonable ways" and it's up to subclasses to ensure everything still works properly. However, it's sometimes necessary to modify a superclass to be "more reasonable" if it's doing things that aren't amenable to being subclassed - this can form a set of principles that help everyone get along well.
Examples
In general, my idea of a "best practice" template for a derived class looks like this:
class Child(Parent):
def __init__(self, arg=1, **kwargs):
self.arg = arg
super().__init__(**kwargs)
That works well in most situations - deal with our stuff, then delegate all the rest to our superclass.
However, it doesn't work well if arg is shared by both Child and Parent - neither the caller's argument nor the Child default are respected:
class Parent:
def __init__(self, arg=0):
self.arg = arg
class Child(Parent):
def __init__(self, arg=1, **kwargs):
self.arg = arg
super().__init__(**kwargs)
print(Child(arg=6).arg)
# Prints `0` - bad
A better approach is probably for Child to acknowledge that the argument is shared:
class Parent:
def __init__(self, arg=0):
self.arg = arg
class Child(Parent):
def __init__(self, arg=1, **kwargs):
super().__init__(arg=arg, **kwargs)
print(Child(arg=6).arg)
# Prints `6` - good
print(Child().arg)
# Prints `1` - good
That successfully gets the defaults working according to expectations. What I'm not sure of is whether this plays well with the expectations of Parent. So I think my questions are:
If Parent.__init__ does some Fancy Stuff with arg and/or self.arg, how should Child be set up to respect that?
In general does this require knowing Too Much about the internals of Parent and how self.arg is used? Or are there reasonable practices that everyone can follow to draw that part of the interface contract in a clean way?
Are there any specific gotchas to keep in mind?
Parent.__init__ only expects that the caller may choose to omit an argument for the arg parameter. It doesn't matter if any particular caller (Child.__init__, in this case) always provides an argument, nor does it matter how the caller produces the value it passes.
Your third example is what I would write, with the addition that Parent.__init__ itself also uses super().__init__: it doesn't assume that it's the end of whatever MRO is in force for its self argument.
class Parent:
def __init__(self, arg=0, **kwargs):
super().__init__(**kwargs)
self.arg = arg
class Child(Parent):
def __init__(self, arg=1, **kwargs):
super().__init__(arg=arg, **kwargs)
I'm trying to wrap my head around how to utilize inheritance in some code I'm writing for an API. I have the following parent class which holds a bunch of common variables that I'd like to instantiate once, and inherit with other classes to make my code look cleaner:
class ApiCommon(object):
def __init__(self, _apikey, _serviceid=None, _vclversion=None,
_aclname=None, _aclid=None):
self.BaseApiUrl = "https://api.fastly.com"
self.APIKey = _apikey
self.headers = {'Fastly-Key': self.APIKey}
self.ServiceID = _serviceid
self.VCLVersion = _vclversion
self.ACLName = _aclname
self.ACLid = _aclid
self.Data = None
self.IP = None
self.CIDR = None
self.fullurl = None
self.r = None
self.jsonresp = None
self.ACLcomment = None
self.ACLentryid = None
And I am inheriting it in another class below, like so in a lib file called lib/security.py:
from apicommon import ApiCommon
class EdgeAclControl(ApiCommon):
def __init__(self):
super(EdgeAclControl, self).__init__()
...
def somemethodhere(self):
return 'stuff'
When I instantiate an object for ApiCommon(object), I can't access the methods in EdgeAclControl(ApiCommon). Example of what I'm trying which isn't working:
from lib import security
gza = security.ApiCommon(_aclname='pytest', _apikey='mykey',
_serviceid='stuffhere', _vclversion=5)
gza.somemethodhere()
How would I instantiate ApiCommon and have access to the methods in EdgeAclControl?
Your current code appears to be trying to use inheritance backwards. When you create an instance of ApiCommon, it will only get the methods defined in that base class. If you want to get methods from a subclass, you need to create an instance of the subclass instead.
So the first fix you need to make is to change gza = security.ApiCommon(...) to gza = EdgeAclControl(...) (though depending on how you're doing your imports, you might need to prefix the class name with a module).
The second issue is that your EdgeAclControl class doesn't take the arguments that its base class needs. Your current code doesn't pass any arguments to super(...).__init__, which doesn't work since the _apikey parameter is required. You could repeat all the arguments again in the subclass, but a lot of the time it's easier to use variable-argument syntax instead.
I suggest that you change EdgeAclControl.__init__ to accept *args and/or **kwargs and pass on those variable arguments when it calls its parent's __init__ method using super. That would look like this:
def __init__(self, *args, **kwargs):
super(EdgeAclControl, self).__init__(*args, **kwargs)
Note that if, as in this example, you're not doing anything other than calling the parent __init__ method in the derived __init__ method, you could get the same effect by just deleting the derived version entirely!
It's likely that your real code does something in EdgeAclControl.__init__, so you may need to keep it in some form. Note that it can take arguments normally in addition to the *args and **kwargs. Just remember to pass on the extra arguments, if necessary, when calling the base class.
May I ask why you have to instantiate an ApiCommon object? I don't see any point of doing so.
If you insist doing that, you have to add methods in superclass and then subclass may override theses methods. But you still couldn't access methods of EdgeAclControl from ApiCommon object
First things first, I'm reasonably new to python, but I have been working hard and doing lots of tutorials and sample projects to get better, so, if I'm missing something obvious, I appologize.
I've been trying to figure this out for a while now, and I've done a number of searches here and through the googles, but I can't quite figure out how to turn the examples I've found into what I'm looking for, so I was hoping someone here could give me a push in the right direction.
class Super1:
def __init__(self,artib1,atrib2,atrib3):
self.atrib1 = atrib1
self.atrib2 = atrib2
self.atrib3 = atrib3
class Sub1(Super1):
def __init__(self,atrib4,atrib5,atrib6)
self.atrib4 = atrib4
self.atrib5 = atrib5
self.atrib6 = atrib6
okay, so what I'm having trouble figuring out is, in the tutroials I've done, they said that I could call on the class like this:
spam = Super1("eggs","foo","bar")
and if I input
print spam.atrib1
it would spit out
eggs
What I want to do is make spam = Sub1, but I don't know how to call it so that I can set all the 'attrib's the way I did with Super1.
I looked up a number of 'multiple inheritance' examples, but I can't seem to reconcile the examples into my own needs. Most of the tutorials don't have more than 1 atribute, or often have the sub 'override' the atributes of the super.
I also checked into composition, and I'm not sure that's exactly what I'm looking for for this part of my project, but I do know that I will need it in later parts.
If anyone can point me in the right direction, that would be great.
You need to call the parent class's constructor Super1.__init__(self)
You also need to allow Sub1 to take the arguments for the parent class's constructor.
With the modifications above, your code becomes:
class Sub1(Super1):
def __init__(self, artib1, atrib2, atrib3, atrib4, atrib5, atrib6)
Super1.__init__(self, artib1, atrib2, atrib3)
self.atrib4 = atrib4
self.atrib5 = atrib5
self.atrib6 = atrib6
However, rather than calling the parent class's constructor yourself, you should use the super built-in function:
super(Sub1, self).__init__(artib1, atrib2, atrib3)
That way, you don't have to hard-code the name of the parent class in each sub-classes constructor. This allows you to easily refactor your code. Another added benefit of using super is that will automatically deal with the sticky details of multiple-inheritance problems such as "diamond inheritance".
One more piece of advice is that if you don't know the amount of positional arguments ahead of time that the super class will take, you can use the *args syntax:
class Sub1(Super1):
def __init__(self, atrib4, atrib5, atrib6, *args)
super(Sub1, self).__init__(*args)
self.atrib4 = atrib4
self.atrib5 = atrib5
self.atrib6 = atrib6
If Sub1 inherits from Super1, that's supposed to mean it is a Super1 (with some extra stuff added, or with some customizations). But you can't remove things, so Sub1 must
contain everything a Super1 contains
initialize the Super1 part of itself by calling super(Sub1,self).1.__init__(self, ...) in its own constructor.
So, if you your super class has a member a, whose value is passed to its constructor, your subclass also has (inherits) a member a, and must somehow pass its value to the superclass constructor.
Whether that means
class Sub1(Super1):
def __init__(self, a, b, c, d, e, f):
super(Sub1, self).__init__(a,b,c)
self.d=d
self.e=e
self.f=f
or whether there's some relationship between the super and subclass arguments (or the subclass hard-codes some of the superclass arguments, or ...) depends on your code.
If you call spam = Super1("eggs","foo","bar"). It will call Super class constructor.
The problem is if you want to create a instance for the Sub1 you should spam = Super1("eggs","foo","bar",atrib4,atrib5,atri6). Also you have to change the constructor for the Sub1 as:
def __init__(self,atrib1,atrib2,atrib3,atrib4,atrib5,atrib6):
Super1.__init__(self,atrib1,atrib2,atrib3)
self.atrib4 = atrib4
self.atrib5 = atrib5
self.atrib6 = atrib6`
According to the python documentation, __init__ can accept varargs:
object.__init__(self[, ...])
Called after the instance has been created (by __new__()), but before it is returned to the caller. The arguments are those passed to the class constructor expression. If a base class has an __init__() method, the derived class’s __init__() method, if any, must explicitly call it to ensure proper initialization of the base class part of the instance; for example:
BaseClass.__init__(self, [args...]).
I'm struggling to understand how to use varargs on classes that directly derive from object. In particular, when trying to instantiate a class that calls super(<classname>, self).__init__(*args, **kwargs), I find that if I instantiate the class with no arguments, everything is ok.
However, if I pass arguments to the init function, I get a the error:
super(A,self).__init__(*args,**kwargs)
TypeError: object.__init__() takes no parameters
It is my understanding that object.__init__ should be able to take parameters based on the documentation -- it also makes writing code easier, as every class can pass its arguments up the class hierarchy.
Is the documentation incorrect, or is object.__init__ a special case?
Code is below:
class A(object):
def __init__(self, *args, **kwargs):
for i,a in enumerate(args):
print "arg", i,a
for k,v in kwargs.iteritems():
print "kwarg", k,v
super(A,self).__init__(*args,**kwargs)
a=A()
print "Done with first one"
a2=A(5,4,5,3)
There is a problem with your interpretation of the docs. In your first line object.__init__(self[, ...]) is not referring to varargs. It is just saying that there may be arguments other than self to __init__.
__init__ is not different from any other method in terms of how it is called, by super, or any other means. For a less confusing example, take something like abs. If you pass more than one argument to abs, it will raise TypeError: abs() takes exactly one argument (2 given). This is normal and expected. If you pass more arguments to an __init__ method than it expects, you will get the same error.
A Python method that is not stated to be able to accept varargs will not be able to accept arbitrary arguments. object.__init__ accepts no arguments. You can all it from your __init__ as either object.__init__(self) or super(type(self), self).__init__().
You can rewrite your example to look like this:
class A(object):
def __init__(self, *args, **kwargs):
for i, a in enumerate(args):
print "arg", i, a
for k, v in kwargs.iteritems():
print "kwarg", k, v
super(A, self).__init__()
This looks pointless at first, since you are extending a class that requires no initialization (object). However, even this simple example shows that you can process some of the arguments for your class, and pass others to the base class.
A common idiom is to explicitly name all the child class's arguments (both positional and keyword-only), and let the parent constructor deal with the remainder. For example, consider the following:
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
class Citizen(Person):
def __init__(self, nationality, *args, **kwargs):
self.nationality = nationality
super(Citizen, self).__init__(*args, **kwargs)
Declaring Citizen to accept varargs in addition to the normal arguments is very convenient in this case. You don't have to remember what arguments Person accepts, or modify anything if they change. It does not mean that you can call Citizen.__init__ with completely arbitrary arguments, however. You still have to pass in a total of three arguments, and keywords can only have the names. nationality, name and age. Notice that Person.__init__ does not even bother to call object.__init__.
The important thing to keep in mind is that all the real classes you will be dealing with, whether from the Python libraries or external sources, should be well documented enough to tell you what you can and can't pass in. Just because something technically accepts varargs, does not mean that it does not have restrictions on what can be passed in.
object is only a special case in that there is nothing to initialize, so its constructor doesn't accept any args or kwargs. Since the __init__ method on object doesn't do anything there's no need to call it if you're inheriting only from object.
super() is defective by design IMHO. See here: https://fuhm.net/super-harmful/
In short:
it interoperates badly with the old calling style
you must never forget it
you cannot change method signature on subclasses
you cannot even be sure in what order the methods will be called
multiple-inheritance is a recipe for confusion anyways.
You wrote the code, you should know the hierarchy. Just use MySuperclass.__foobar__(self, etc). Works all the time.
Why did the Python designers decide that subclasses' __init__() methods don't automatically call the __init__() methods of their superclasses, as in some other languages? Is the Pythonic and recommended idiom really like the following?
class Superclass(object):
def __init__(self):
print 'Do something'
class Subclass(Superclass):
def __init__(self):
super(Subclass, self).__init__()
print 'Do something else'
The crucial distinction between Python's __init__ and those other languages constructors is that __init__ is not a constructor: it's an initializer (the actual constructor (if any, but, see later;-) is __new__ and works completely differently again). While constructing all superclasses (and, no doubt, doing so "before" you continue constructing downwards) is obviously part of saying you're constructing a subclass's instance, that is clearly not the case for initializing, since there are many use cases in which superclasses' initialization needs to be skipped, altered, controlled -- happening, if at all, "in the middle" of the subclass initialization, and so forth.
Basically, super-class delegation of the initializer is not automatic in Python for exactly the same reasons such delegation is also not automatic for any other methods -- and note that those "other languages" don't do automatic super-class delegation for any other method either... just for the constructor (and if applicable, destructor), which, as I mentioned, is not what Python's __init__ is. (Behavior of __new__ is also quite peculiar, though really not directly related to your question, since __new__ is such a peculiar constructor that it doesn't actually necessarily need to construct anything -- could perfectly well return an existing instance, or even a non-instance... clearly Python offers you a lot more control of the mechanics than the "other languages" you have in mind, which also includes having no automatic delegation in __new__ itself!-).
I'm somewhat embarrassed when people parrot the "Zen of Python", as if it's a justification for anything. It's a design philosophy; particular design decisions can always be explained in more specific terms--and they must be, or else the "Zen of Python" becomes an excuse for doing anything.
The reason is simple: you don't necessarily construct a derived class in a way similar at all to how you construct the base class. You may have more parameters, fewer, they may be in a different order or not related at all.
class myFile(object):
def __init__(self, filename, mode):
self.f = open(filename, mode)
class readFile(myFile):
def __init__(self, filename):
super(readFile, self).__init__(filename, "r")
class tempFile(myFile):
def __init__(self, mode):
super(tempFile, self).__init__("/tmp/file", mode)
class wordsFile(myFile):
def __init__(self, language):
super(wordsFile, self).__init__("/usr/share/dict/%s" % language, "r")
This applies to all derived methods, not just __init__.
Java and C++ require that a base class constructor is called because of memory layout.
If you have a class BaseClass with a member field1, and you create a new class SubClass that adds a member field2, then an instance of SubClass contains space for field1 and field2. You need a constructor of BaseClass to fill in field1, unless you require all inheriting classes to repeat BaseClass's initialization in their own constructors. And if field1 is private, then inheriting classes can't initialise field1.
Python is not Java or C++. All instances of all user-defined classes have the same 'shape'. They're basically just dictionaries in which attributes can be inserted. Before any initialisation has been done, all instances of all user-defined classes are almost exactly the same; they're just places to store attributes that aren't storing any yet.
So it makes perfect sense for a Python subclass not to call its base class constructor. It could just add the attributes itself if it wanted to. There's no space reserved for a given number of fields for each class in the hierarchy, and there's no difference between an attribute added by code from a BaseClass method and an attribute added by code from a SubClass method.
If, as is common, SubClass actually does want to have all of BaseClass's invariants set up before it goes on to do its own customisation, then yes you can just call BaseClass.__init__() (or use super, but that's complicated and has its own problems sometimes). But you don't have to. And you can do it before, or after, or with different arguments. Hell, if you wanted you could call the BaseClass.__init__ from another method entirely than __init__; maybe you have some bizarre lazy initialization thing going.
Python achieves this flexibility by keeping things simple. You initialise objects by writing an __init__ method that sets attributes on self. That's it. It behaves exactly like a method, because it is exactly a method. There are no other strange and unintuitive rules about things having to be done first, or things that will automatically happen if you don't do other things. The only purpose it needs to serve is to be a hook to execute during object initialisation to set initial attribute values, and it does just that. If you want it to do something else, you explicitly write that in your code.
To avoid confusion it is useful to know that you can invoke the base_class __init__() method if the child_class does not have an __init__() class.
Example:
class parent:
def __init__(self, a=1, b=0):
self.a = a
self.b = b
class child(parent):
def me(self):
pass
p = child(5, 4)
q = child(7)
z= child()
print p.a # prints 5
print q.b # prints 0
print z.a # prints 1
In fact the MRO in python will look for __init__() in the parent class when can not find it in the children class. You need to invoke the parent class constructor directly if you have already an __init__() method in the children class.
For example the following code will return an error:
class parent:
def init(self, a=1, b=0):
self.a = a
self.b = b
class child(parent):
def __init__(self):
pass
def me(self):
pass
p = child(5, 4) # Error: constructor gets one argument 3 is provided.
q = child(7) # Error: constructor gets one argument 2 is provided.
z= child()
print z.a # Error: No attribute named as a can be found.
"Explicit is better than implicit." It's the same reasoning that indicates we should explicitly write 'self'.
I think in in the end it is a benefit-- can you recite all of the rules Java has regarding calling superclasses' constructors?
Right now, we have a rather long page describing the method resolution order in case of multiple inheritance: http://www.python.org/download/releases/2.3/mro/
If constructors were called automatically, you'd need another page of at least the same length explaining the order of that happening. That would be hell...
Often the subclass has extra parameters which can't be passed to the superclass.
Maybe __init__ is the method that the subclass needs to override. Sometimes subclasses need the parent's function to run before they add class-specific code, and other times they need to set up instance variables before calling the parent's function. Since there's no way Python could possibly know when it would be most appropriate to call those functions, it shouldn't guess.
If those don't sway you, consider that __init__ is Just Another Function. If the function in question were dostuff instead, would you still want Python to automatically call the corresponding function in the parent class?
i believe the one very important consideration here is that with an automatic call to super.__init__(), you proscribe, by design, when that initialization method is called, and with what arguments. eschewing automatically calling it, and requiring the programmer to explicitly do that call, entails a lot of flexibility.
after all, just because class B is derived from class A does not mean A.__init__() can or should be called with the same arguments as B.__init__(). making the call explicit means a programmer can have e.g. define B.__init__() with completely different parameters, do some computation with that data, call A.__init__() with arguments as appropriate for that method, and then do some postprocessing. this kind of flexibility would be awkward to attain if A.__init__() would be called from B.__init__() implicitly, either before B.__init__() executes or right after it.
As Sergey Orshanskiy pointed out in the comments, it is also convenient to write a decorator to inherit the __init__ method.
You can write a decorator to inherit the __init__ method, and even perhaps automatically search for subclasses and decorate them. – Sergey Orshanskiy Jun 9 '15 at 23:17
Part 1/3: The implementation
Note: actually this is only useful if you want to call both the base and the derived class's __init__ since __init__ is inherited automatically. See the previous answers for this question.
def default_init(func):
def wrapper(self, *args, **kwargs) -> None:
super(type(self), self).__init__(*args, **kwargs)
return wrapper
class base():
def __init__(self, n: int) -> None:
print(f'Base: {n}')
class child(base):
#default_init
def __init__(self, n: int) -> None:
pass
child(42)
Outputs:
Base: 42
Part 2/3: A warning
Warning: this doesn't work if base itself called super(type(self), self).
def default_init(func):
def wrapper(self, *args, **kwargs) -> None:
'''Warning: recursive calls.'''
super(type(self), self).__init__(*args, **kwargs)
return wrapper
class base():
def __init__(self, n: int) -> None:
print(f'Base: {n}')
class child(base):
#default_init
def __init__(self, n: int) -> None:
pass
class child2(child):
#default_init
def __init__(self, n: int) -> None:
pass
child2(42)
RecursionError: maximum recursion depth exceeded while calling a Python object.
Part 3/3: Why not just use plain super()?
But why not just use the safe plain super()? Because it doesn't work since the new rebinded __init__ is from outside the class, and super(type(self), self) is required.
def default_init(func):
def wrapper(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
return wrapper
class base():
def __init__(self, n: int) -> None:
print(f'Base: {n}')
class child(base):
#default_init
def __init__(self, n: int) -> None:
pass
child(42)
Errors:
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-9-6f580b3839cd> in <module>
13 pass
14
---> 15 child(42)
<ipython-input-9-6f580b3839cd> in wrapper(self, *args, **kwargs)
1 def default_init(func):
2 def wrapper(self, *args, **kwargs) -> None:
----> 3 super().__init__(*args, **kwargs)
4 return wrapper
5
RuntimeError: super(): __class__ cell not found
Background - We CAN AUTO init a parent AND child class!
A lot of answers here and say "This is not the python way, use super().__init__() from the subclass". The question is not asking for the pythonic way, it's comparing to the expected behavior from other languages to python's obviously different one.
The MRO document is pretty and colorful but it's really a TLDR situation and still doesn't quite answer the question, as is often the case in these types of comparisons - "Do it the Python way, because.".
Inherited objects can be overloaded by later declarations in subclasses, a pattern building on #keyvanrm's (https://stackoverflow.com/a/46943772/1112676) answer solves the case where I want to AUTOMATICALLY init a parent class as part of calling a class without explicitly calling super().__init__() in every child class.
In my case where a new team member might be asked to use a boilerplate module template (for making extensions to our application without touching the core application source) which we want to make as bare and easy to adopt without them needing to know or understand the underlying machinery - to only need to know of and use what is provided by the application's base interface which is well documented.
For those who will say "Explicit is better than implicit." I generally agree, however, when coming from many other popular languages inherited automatic initialization is the expected behavior and it is very useful if it can be leveraged for projects where some work on a core application and others work on extending it.
This technique can even pass args/keyword args for init which means pretty much any object can be pushed to the parent and used by the parent class or its relatives.
Example:
class Parent:
def __init__(self, *args, **kwargs):
self.somevar = "test"
self.anothervar = "anothertest"
#important part, call the init surrogate pass through args:
self._init(*args, **kwargs)
#important part, a placeholder init surrogate:
def _init(self, *args, **kwargs):
print("Parent class _init; ", self, args, kwargs)
def some_base_method(self):
print("some base method in Parent")
self.a_new_dict={}
class Child1(Parent):
# when omitted, the parent class's __init__() is run
#def __init__(self):
# pass
#overloading the parent class's _init() surrogate
def _init(self, *args, **kwargs):
print(f"Child1 class _init() overload; ",self, args, kwargs)
self.a_var_set_from_child = "This is a new var!"
class Child2(Parent):
def __init__(self, onevar, twovar, akeyword):
print(f"Child2 class __init__() overload; ", self)
#call some_base_method from parent
self.some_base_method()
#the parent's base method set a_new_dict
print(self.a_new_dict)
class Child3(Parent):
pass
print("\nRunning Parent()")
Parent()
Parent("a string", "something else", akeyword="a kwarg")
print("\nRunning Child1(), keep Parent.__init__(), overload surrogate Parent._init()")
Child1()
Child1("a string", "something else", akeyword="a kwarg")
print("\nRunning Child2(), overload Parent.__init__()")
#Child2() # __init__() requires arguments
Child2("a string", "something else", akeyword="a kwarg")
print("\nRunning Child3(), empty class, inherits everything")
Child3().some_base_method()
Output:
Running Parent()
Parent class _init; <__main__.Parent object at 0x7f84a721fdc0> () {}
Parent class _init; <__main__.Parent object at 0x7f84a721fdc0> ('a string', 'something else') {'akeyword': 'a kwarg'}
Running Child1(), keep Parent.__init__(), overload surrogate Parent._init()
Child1 class _init() overload; <__main__.Child1 object at 0x7f84a721fdc0> () {}
Child1 class _init() overload; <__main__.Child1 object at 0x7f84a721fdc0> ('a string', 'something else') {'akeyword': 'a kwarg'}
Running Child2(), overload Parent.__init__()
Child2 class __init__() overload; <__main__.Child2 object at 0x7f84a721fdc0>
some base method in Parent
{}
Running Child3(), empty class, inherits everything, access things set by other children
Parent class _init; <__main__.Child3 object at 0x7f84a721fdc0> () {}
some base method in Parent
As one can see, the overloaded definition(s) take the place of those declared in Parent class but can still be called BY the Parent class thereby allowing one to emulate the classical implicit inheritance initialization behavior Parent and Child classes both initialize without needing to explicitly invoke the Parent's init() from the Child class.
Personally, I call the surrogate _init() method main() because it makes sense to me when switching between C++ and Python for example since it is a function that will be automatically run for any subclass of Parent (the last declared definition of main(), that is).