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


我正在打扰 __new__() 返回具有特定类的类实例的类的方法 __init__() 组。 Python似乎称之为提供的类 __init__() 方法而不是特定于实例的方法,虽然Python文档在

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

说:

典型的实现通过调用来创建类的新实例   superclass的__new __()方法使用super(currentclass,cls).__ new __(cls [,...])   使用适当的参数,然后将新创建的实例修改为   在返回之前必要的。

如果__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() 运行,是明确地称之为 self.__init__() 内 myclass.__init__()


4993
2017-07-24 16:41


起源

好问题! - Marcin


答案:


关于新样式类的特殊方法是在实例的类型上查找,而不是在实例本身上查找。这是 记录的行为

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

>>> 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()

8
2017-07-24 16:45





各种特殊方法(包括 __init__,还有运算符重载等 __add__等等) 总是通过类而不是实例访问。不仅如此,他们无法通过访问 __getattr__ 要么 __getattribute__ 类或元类的方法,它们必须直接在类上。这是出于效率的原因:

绕过 __getattribute__() 这种方式的机器为解释器内的速度优化提供了很大的空间,代价是处理特殊方法的一些灵活性(必须在类对象本身上设置特殊方法,以便由解释器一致地调用)。

你要完成的是什么并不完全清楚,但你可以做的一件事是继承 myclass 在...内 __new__ 方法:

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)

8
2017-07-24 16:45