Подтвердить что ты не робот

Проблема с созданием объекта, вызываемого в python

Я написал код, подобный этому

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

Ответ 3

Как насчет этого? Определить базовый класс 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