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

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

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

говорит:

Типичные реализации создают новый экземпляр класса, вызывая метод суперкласса __new __ (), используя super (currentclass, cls) .__ new __ (cls [, ...]) с соответствующими аргументами, а затем при необходимости изменяя вновь созданный экземпляр перед возвратом. Это.

Если __new __ () возвращает экземпляр cls, тогда метод __init __ () нового экземпляра будет вызываться как __init __ (self [, ...]), где self - это новый экземпляр, а остальные аргументы такие же, как были переданы в __новый__().

Вот мой тестовый код:

#!/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__().


person vkoukis    schedule 24.07.2012    source источник


Ответы (3)


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

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

>>> 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()
person Sven Marnach    schedule 24.07.2012

Различные специальные методы (включая __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)
person ecatmur    schedule 24.07.2012

Почему 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 dict. То же самое и с методом __init__.

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

person prosti    schedule 04.06.2019