简体   繁体   中英

`__new__` and `__init__` on class and object

In Python 3, when defining a subclass, why do you need to use cls as the first argument of __new__ , but not to use self as the first argument of __init__ ?

An example:

class MyClass(object):
    def __new__(cls, *args, **kwargs):
        return super(MyClass, cls).__new__(cls, *args, **kwargs) # with `cls`
    def __init__(self, *args, **kwargs):
        return super(MyClass, self).__init__(*args, **kwargs) # without `self`

When I compared these functions I got more confused:

>>> cls = object
>>> self = cls()
>>> cls.__new__ is self.__new__
True
>>> cls.__init__ is self.__init__
False
>>> self.__init__()
>>> cls.__init__()
Traceback (most recent call last): ...

So, what are the differences between __new__ and __init__ behind these results? Which methods are bound and which are free? Why are you able to call self.__init__() but not cls.__init__() ? Is cls.__init__ a method defined in cls itself or in its metaclass?

The biggest part of the picture you're probably missing is that __new__ is a staticmethod, special-cased to be one even if you don't use the @staticmethod decorator.

When calling a method through super() , super() performs the same kind of argument binding that would be performed normally for that kind of method (using the descriptor protocol ). For a staticmethod like __new__ , that means no arguments are automatically bound, so cls has to be passed explicitly. For an instance method like __init__ , that means self is bound automatically, which is why you don't have to pass self to super().__init__ .

The main purpose of __new__ is to allocate a new instance of the class, while __init__ 's job is to set up an existing instance.

According to the docs:

__new__() is a static method (special-cased so you need not declare it as such)

__init__ on the other hand, is a proper instance method. It can be called multiple times on the same instance, by the way.

That should be enough to explain your terminal session:

>>> cls = object
>>> self = cls()

You just called object.__call__ , which essentially does

self = cls.__new__()
if isinstance(self, cls):
    cls.__init__(self)
return self

Notice that the return value of __new__ is not required to be an instance of the class it belongs to, but __init__ is called only if it is. In your case, it is.

>>> cls.__new__ is self.__new__
True

__new__ is a static method, so attempting to bind it to the instance does nothing: it stays a class method. This is the same reason that you have to pass cls explicitly when calling super().__new__ : it's the same free function, unbound to either class or instance.

 >>> cls.__init__ is self.__init__
 False

Not only are these not the same thing, but their types are different. cls.__init__ is a regular function. self.__init__ is a bound method which lacks the first parameter of cls.__init__ .

>>> self.__init__()

This has already been called, but for object , it's a no-op you can call as many times as you like. Notice that the first parameter is not being passed in, being as it is a bound method.

>>> cls.__init__()

This is calling the raw __init__ function, which requires that the self parameter be passed in. Since you don't do that, it raises. Try this instead:

>>> cls.__init__(self)

cls stands for class itself, while self stands for object itself. These are just conventions.
The __new__ method is called before the object is created, in fact, __new__ should create the object and return it. Therefore, it needs a class to create object. After that, the __init__ is called to initialize the object, so it needs the object as the first argument.

For example:

class MyClass:
    def __new__(cls, *args, **kwargs):
        # cls == MyClass
        return super().__new__(cls, *args, **kwargs)
        # cls here is because __new__ is staticmethods so you have to pass the cls explicitly

        # You can't use cls() here because it will call this methods again and again
        # causing recusion error

    def __init__(self, *args, **kwargs):
        # Here self is the instance(or object) of MyClass
        # So you can initialize it by self.xxx
        self.xxx = 'xxx'

__new__ is static method, so the class and instance share the same __new__ method. __init__ is the method of instance. If you want to call it via class, you need to pass the instance as the first argument explicitly.

cls.__init__(self)

Anything in Python is object, including the class itself. So for class, it has its own __new__ and __init__ which are used by metaclass to create class and initialize class.
These are meta programming of Python, I suggest to read the ninth chapter of Python Cookbook.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM