Проблема с созданием объекта, вызываемого в Python - PullRequest
18 голосов
/ 10 ноября 2009

Я написал такой код

>>> class a(object):
        def __init__(self):
            self.__call__ = lambda x:x

>>> b = a()

Я ожидал, что объект класса a должен быть вызываемым объектом, но в конечном итоге это не так.

>>> b()

Traceback (most recent call last):
    File "<pyshell#5>", line 1, in <module>
        b()
TypeError: 'a' object is not callable
>>> callable(b)
False

>>> hasattr(b,'__call__')
True
>>> 

Я не могу понять, почему. Пожалуйста, помогите мне.

Ответы [ 3 ]

19 голосов
/ 10 ноября 2009

Специальные методы ищутся для типа (например, класса) объекта, с которым выполняется операция, а не для конкретного экземпляра. Подумайте об этом: в противном случае, если класс определяет, например, __call__, когда класс вызывается, то __call__ должен вызываться ... какая катастрофа! Но, к счастью, специальный метод вместо этого ищется по типу класса, метаклассу AKA, и все хорошо («унаследованные классы» вели себя нерегулярно, поэтому нам всем лучше с новыми стилями - которые остались только в Python 3).

Так что, если вам нужно "переопределение для каждого экземпляра" специальных методов, вы должны убедиться, что экземпляр имеет свой собственный уникальный класс. Это очень просто:

class a(object):
    def __init__(self):
        self.__class__ = type(self.__class__.__name__, (self.__class__,), {})
        self.__class__.__call__ = lambda x:x

и ты там. Конечно, это было бы глупо в этом случае, так как каждый экземпляр заканчивается одинаковыми «так называемыми для каждого экземпляра» (!) __call__, но было бы полезно, если бы вам действительно нужно было переопределить индивидуальное основа экземпляра.

14 голосов
/ 10 ноября 2009

__call__ необходимо определить для класса, а не для экземпляра

class a(object):
    def __init__(self):
        pass
    __call__ = lambda x:x

но большинство людей, вероятно, сочтут более понятным определение метода обычным способом

class a(object):
    def __init__(self):
        pass
    def __call__(self):
        return self

Если вам нужно иметь различное поведение для каждого экземпляра, вы можете сделать это следующим образом

class a(object):
    def __init__(self):
        self.myfunc = lambda x:x

    def __call__(self):
        return self.myfunc(self)
1 голос
/ 21 марта 2011

А как насчет этого? Определите базовый класс AllowDynamicCall:

class AllowDynamicCall(object):
     def __call__(self, *args, **kwargs):
         return self._callfunc(self, *args, **kwargs)

А затем подкласс AllowDynamicCall:

class Example(AllowDynamicCall):
     def __init__(self):
         self._callfunc = lambda s: s
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...