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

Как сделать подкласс 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__ для некоторого не вызываемого значения по-прежнему приводят к появлению дочернего элемента как вызываемого.

4b9b3361

Ответ 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

Которая все равно будет вызываемой, но просто будет нулевой.