Как сделать подкласс Python некорректным
Как вы отключите метод __call__
в подклассе, чтобы было верно следующее:
class Parent(object):
def __call__(self):
return
class Child(Parent):
def __init__(self):
super(Child, self).__init__()
object.__setattr__(self, '__call__', None)
>>> c = Child()
>>> callable(c)
False
Этот и другие способы попыток установить __call__
для некоторого не вызываемого значения по-прежнему приводят к появлению дочернего элемента как вызываемого.
Ответы
Ответ 1
Вы не можете. Как отмечает jonrsharpe, не существует способа заставить Child
не иметь атрибута, а то, на что callable(Child())
полагается, чтобы дать свой ответ. Даже делает его дескриптором, который поднимает AttributeError
, не будет работать, за этот отчет об ошибке: https://bugs.python.org/issue23990. Пример python 2:
>>> class Parent(object):
... def __call__(self): pass
...
>>> class Child(Parent):
... __call__ = property()
...
>>> c = Child()
>>> c()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: unreadable attribute
>>> c.__call__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: unreadable attribute
>>> callable(c)
True
Это потому, что callable(...)
не выполняет протокол дескриптора. Фактически вызов объекта или доступ к атрибуту __call__
включает в себя извлечение метода, даже если он находится за свойством, через стандартный протокол дескриптора. Но callable(...)
не беспокоится о том, что так далеко, если он находит что-либо вообще, он удовлетворен, и каждый подкласс Parent будет иметь что-то для __call__
- либо атрибут в подклассе, либо определение из Parent
.
Итак, если вы действительно можете вызвать экземпляр с ошибкой с любым желаемым вами исключением, вы никогда не сможете сделать callable(some_instance_of_parent)
return False.
Ответ 2
Неплохая идея изменить общедоступный интерфейс класса так радикально от родителя к базе.
Как указывалось в другом месте, вы не можете выполнить uninherit __call__
. Если вам действительно нужно смешивать в вызываемых и не вызываемых вызовах классах, вы должны использовать другой тест (добавление атрибута класса) или просто сделать его безопасным для вызова вариантов без каких-либо функций.
Чтобы сделать последнее, вы можете переопределить __call__
, чтобы поднять NotImplemented
(или, лучше, собственное исключение из собственного), если по какой-то причине вы хотели бы смешать класс без вызова с вызываемыми вариантами:
class Parent(object):
def __call__(self):
print "called"
class Child (Parent):
def __call__(self):
raise NotACallableInstanceException()
for child_or_parent in list_of_children_and_parents():
try:
child_or_parent()
except NotACallableInstanceException:
pass
Или просто переопределите вызов с помощью pass:
class Parent(object):
def __call__(self):
print "called"
class Child (Parent):
def __call__(self):
pass
Которая все равно будет вызываемой, но просто будет нулевой.