Reputation: 127
I'm learning python and the construct method __init__
makes me confused.
class Test(object):
def __init__(self):
pass
As known, python implicitly pass a parameter representing the object itself at the first place. It is acceptable in common member functions. But what makes me confused is that the constructor requests an object itself.
It looks like the c++ code below. This code does not make sense! The parameter thiz
does not even exit before the constructor!
class Test {
public:
Test(Test *thiz);
};
So, my question is, why python needs a "non-existent" self
object in constructor __init__
?
Upvotes: 1
Views: 420
Reputation: 114230
__init__
behaves like any other normal function in a class. It gets called by the interpreter at a special time if it is defined, but there is nothing special about it. You can call it any time yourself, just like a regular method.
Here are some facts to help you see the picture:
Function objects are non-data descriptors in python. That means that function objects have a __get__
method, but not __set__
. And if course they have a __call__
method to actually run the function.
When you put a def
statement in a class body, it creates a regular function, just like it would elsewhere. You can see this by looking at type(Test.__init__)
. To call Test.__init__
, you would have to manually pass in a self
parameter.
The magic happens when you call __init__
on an instance of Test
. For example:
a = Test()
a.__init__()
This code actually calls __init__
twice (which we'll get into momentarily). The explicit second call does not pass in a parameter to the method. That's because a.__init__
has special meaning when the name __init__
is present in the class but not the instance. If __init__
is a descriptor, the interpreter will not return it directly, but rather will call __get__
on it and return the result. This is called binding.
a.__init__()
is roughly equivalent to type(a).__init__.__get__(a, None)()
. The call .__get__(a, None)
returns a callable object that is a bound method. It is like a special type of partial function, in which the first positional parameter is set to a
. You can see this by checking type(a.__init__)
.
The self
parameter is passed to methods as the first positional parameter. As such, it does not have to be called self
. The name is just a convention. def __init__(this):
or def __init__(x)
would work just as well.
Finally, let's discuss how a = Test()
ends up calling __init__
. Class objects are callable in python. They have a class themselves, called a metaclass (usually just type
), which actually defines a __call__
method. When you do Test()
, you are literally calling the class.
By default, the __call__
method of a class looks something (very roughly approximately) like this:
def __call__(cls, *args, **kwargs):
self = cls.__new__(cls, *args, **kwargs)
if isinstance(self, cls):
type(self).__init__(self, *args, **kwargs)
So a new instance is only created by __new__
, not __init__
. The latter is an initializer, not a constructor or allocator. In fact, as I mentioned before, you can call __init__
as often as you like on most classes. Notable exceptions include immutable built-in classes like tuple
and int
.
As an initializer, __init__
obviously needs access to the object it is initializing. Your example does nothing with self
, but it is pretty standard to set instance attributes and do something things to prep the object. For cases like yours, an explicit __init__
is not necessary at all, since a missing function will be found in the base class hierarchy.
Upvotes: 2