Почему Python не вызывает метод экземпляра __init __() при создании экземпляра, но вызывает вместо него класс __init __()?

Я переопределяю метод __new__() класса, чтобы вернуть экземпляр класса, который имеет определенный набор __init__(). Python, кажется, вызывает метод __init__(), предоставляемый классом, вместо специфичного для экземпляра метода, хотя документация Python в

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

говорит:

Типичные реализации создают новый экземпляр класса, вызывая суперклассы __новый метод __() с использованием 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__().

Ответ 1

Специальные методы в классах нового стиля проверяются на тип экземпляра, а не на сам экземпляр. Это документальное поведение:

Для классов нового стиля неявные вызовы специальных методов гарантируются только с правильной работой, если они определены в типе объектов, а не в словаре экземпляров объектов. Это поведение является причиной того, что следующий код вызывает исключение (в отличие от эквивалентного примера со старыми классами):

>>> 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__ и т.д.) всегда доступны через класс, а не экземпляр. Не только это, но они не могут быть доступны с помощью метода __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)

Ответ 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} является частью dict экземпляра.

'one': 42 принадлежит классу C а также любому экземпляру. То же самое и с методом __init__.

Это за дизайн.