为啥 Python 在创建实例时不调用实例方法 __init__() 而是调用类提供的 __init__() ?

Posted

技术标签:

【中文标题】为啥 Python 在创建实例时不调用实例方法 __init__() 而是调用类提供的 __init__() ?【英文标题】:Why doesn't Python call instance method __init__() on instance creation but calls class-provided __init__() instead?为什么 Python 在创建实例时不调用实例方法 __init__() 而是调用类提供的 __init__() ? 【发布时间】:2012-07-23 01:03:22 【问题描述】:

我正在覆盖类的 __new__() 方法以返回具有特定 __init__() 集的类实例。 Python 似乎调用了类提供的 __init__() 方法而不是特定于实例的方法,尽管 Python 文档位于

http://docs.python.org/reference/datamodel.html

说:

典型的实现通过调用 使用 super(currentclass, cls).__new__(cls[, ...]) 的超类的 __new__() 方法 使用适当的参数,然后将新创建的实例修改为 退货前需要。

如果 __new__() 返回一个 cls 的实例,那么新实例的 __init__() 方法 将像 __init__(self[, ...]) 一样被调用,其中 self 是新实例,而 其余参数与传递给 __new__() 的参数相同。

这是我的测试代码:

#!/usr/bin/env python

import new

def myinit(self, *args, **kwargs):
    print "myinit called, args = %s, kwargs = %s" % (args, kwargs)


class myclass(object):
    def __new__(cls, *args, **kwargs):
        ret = object.__new__(cls)

        ret.__init__ = new.instancemethod(myinit, ret, cls)
        return ret

    def __init__(self, *args, **kwargs):
        print "myclass.__init__ called, self.__init__ is %s" % self.__init__
        self.__init__(*args, **kwargs)

a = myclass()

哪个输出

$ python --version
Python 2.6.6
$ ./mytest.py
myclass.__init__ called, self.__init__ is <bound method myclass.myinit of <__main__.myclass object at 0x7fa72155c790>>
myinit called, args = (), kwargs = 

似乎让myinit() 运行的唯一方法是在myclass.__init__() 内显式调用它为self.__init__()

【问题讨论】:

【参考方案1】:

新式类的特殊方法是在实例的类型上查找的,而不是在实例本身上。这是documented behaviour:

对于新型类,特殊方法的隐式调用只有在对象类型上定义时才能保证正常工作,而不是在对象的实例字典中。这种行为是以下代码引发异常的原因(与旧式类的等效示例不同):

>>> class C(object):
...     pass
...
>>> c = C()
>>> c.__len__ = lambda: 5
>>> len(c)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'C' has no len()

【讨论】:

【参考方案2】:

各种特殊方法(包括__init__,还有__add__等运算符重载)都是always accessed via the class rather than the instance。不仅如此,它们不能通过类或元类上的__getattr____getattribute__ 方法访问,它们必须直接在类上。这是为了提高效率:

以这种方式绕过__getattribute__() 机制为解释器中的速度优化提供了很大的空间,但代价是处理特殊方法时具有一定的灵活性(特殊方法必须在类对象本身上设置才能被解释器始终调用)。

尚不完全清楚您要完成什么,但您可以在这里做的一件事是在 __new__ 方法中子类化 myclass

class myclass(object):
    def __new__(cls, *args, **kwargs):
        class subcls(cls):
            def __new__(cls, *args, **kwargs):
                return object.__new__(cls)
        subcls.__init__ = myinit
        return subcls(*args, **kwargs)

【讨论】:

【参考方案3】:

为什么 Python 在创建实例时不调用实例方法 init() 而是调用类提供的 init()?

看看这个例子。

class C:
   one = 42
   def __init__(self,val):
        self.two=val
ci=C(50)
print(ci.__dict__)
print(C.__dict__)

结果会是这样的:

'two': 50
'__module__': '__main__', 'one': 42, '__init__': <function C.__init__ at 0x00000213069BF6A8>, '__dict__': <attribute '__dict__' of 'C' objects>, '__weakref__': <attribute '__weakref__' of 'C' objects>, '__doc__': None

请注意,只有值 'two': 50 是实例字典的一部分。

'one': 42 属于一个类 C 字典。 __init__ 方法也是如此。

这是每个设计。

【讨论】:

以上是关于为啥 Python 在创建实例时不调用实例方法 __init__() 而是调用类提供的 __init__() ?的主要内容,如果未能解决你的问题,请参考以下文章

创建python对象错误的实例 - 如何调用super方法?

为啥在 python 中使用类方法而不是实例方法

python

为啥在 Python 3 中实例的 __dict__ 的大小要小得多?

Python中的对象行为与特殊方法对象的创建与销毁

python_高级