简体   繁体   English

类和对象上的__new__和__init__

[英]`__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__ ? 在Python 3中,定义子类时,为什么需要将cls用作__new__的第一个参数,而不需要将self用作__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? 那么,这些结果背后的__new____init__有什么区别? Which methods are bound and which are free? 哪些方法是绑定的,哪些是免费的? Why are you able to call self.__init__() but not cls.__init__() ? 为什么可以调用self.__init__()但不能调用cls.__init__() Is cls.__init__ a method defined in cls itself or in its metaclass? cls.__init__是在cls本身或其元类中定义的方法吗?

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. 你可能丢失图片的最大的部分是, __new__是一个静态方法, 特例,是一个即使你不使用@staticmethod装饰。

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 ). 通过super()调用方法时, super()会执行与通常为该方法执行的相同类型的参数绑定(使用描述符协议 )。 For a staticmethod like __new__ , that means no arguments are automatically bound, so cls has to be passed explicitly. 对于像__new__这样的静态__new__ ,这意味着不会自动绑定任何参数,因此必须显式传递cls 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__ . 对于类似__init__的实例方法,这意味着self是自动绑定的,这就是为什么您不必将self传递给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. __new__的主要目的是分配该类的新实例,而__init__的工作是设置现有实例。

According to the docs: 根据文档:

__new__() is a static method (special-cased so you need not declare it as such) __new__()是静态方法(特殊情况,因此您无需这样声明)

__init__ on the other hand, is a proper instance method. 另一方面, __init__是合适的实例方法。 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 您只是调用了object.__call__ ,实际上

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. 请注意, __new__的返回值__new__它所属的类的实例,但是__init__仅在它属于时才被调用。 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. __new__是静态方法,因此尝试将其绑定到实例没有任何作用:它保留为类方法。 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. 这与调用super().__new__时必须显式传递cls原因相同super().__new__ :这是相同的自由函数,不绑定到类或实例。

 >>> 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. cls.__init__是常规函数。 self.__init__ is a bound method which lacks the first parameter of cls.__init__ . self.__init__是一个绑定方法,缺少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. 这已经被调用过,但是对于object ,这是一个空操作,您可以随意调用多次。 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. 这是在调用原始的__init__函数,该函数要求传入self参数。由于您不这样做,它会引发。 Try this instead: 尝试以下方法:

>>> cls.__init__(self)

cls stands for class itself, while self stands for object itself. cls代表类本身,而self代表对象本身。 These are just conventions. 这些只是约定。
The __new__ method is called before the object is created, in fact, __new__ should create the object and return it. __new__方法在对象创建之前被调用,实际上, __new__应该创建对象并返回它。 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. 之后,将调用__init__初始化对象,因此它需要该对象作为第一个参数。

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. __new__是静态方法,因此类和实例共享相同的__new__方法。 __init__ is the method of instance. __init__是实例的方法。 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. Python中的任何东西都是对象,包括类本身。 So for class, it has its own __new__ and __init__ which are used by metaclass to create class and initialize class. 因此,对于类,它具有自己的__new____init__ ,它们由metaclass用于创建类和初始化类。
These are meta programming of Python, I suggest to read the ninth chapter of Python Cookbook. 这些是Python的元编程,我建议阅读Python Cookbook的第9章。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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