Проблема с созданием объекта, вызываемого в 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
>>>
Я не понимаю, почему.
Пожалуйста, помогите мне.
Ответы
Ответ 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