Reputation: 1945
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)
Upvotes: 2
Views: 3079
Reputation: 114478
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.
Upvotes: 4
Reputation: 3136
super()
is defective by design IMHO. See here: https://fuhm.net/super-harmful/
In short:
You wrote the code, you should know the hierarchy. Just use MySuperclass.__foobar__(self, etc)
. Works all the time.
Upvotes: -3
Reputation: 5203
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
.
Upvotes: 1