2016-12-15 2 views
0

Рассмотрим следующий пример кода:Аннотация собственности на __name__ не соблюдается

from abc import ABC, abstractmethod, abstractproperty 

class Base(ABC): 

    @abstractmethod 
    def foo(self) -> str: 
     print("abstract") 

    @property 
    @abstractmethod 
    def __name__(self) -> str: 
     return "abstract" 

    @abstractmethod 
    def __str__(self) -> str: 
     return "abstract" 

    @property 
    @abstractmethod 
    def __add__(self, other) -> str: 
     return "abstract" 


class Sub(Base): 

    def foo(self): 
     print("concrete") 

    def __str__(self): 
     return "concrete" 

    def __add__(self, other) -> str: 
     return "concrete" 


sub = Sub() 
sub.foo() 
sub.__name__ 
print(str(sub)) 

Обратите внимание, что подкласс не реализует абстрактное свойство __name__, и в самом деле, когда __name__ ссылаются, он печатает как «абстрактный» от своего родителя :

>>> sub.foo() 
concrete 
>>> sub.__name__ 
'abstract' 
>>> print(str(sub)) 
concrete 

Однако, это не потому, что __name__ является метод Dunder, ни из-за какой-то вопрос с @property и @abstractmethod декораторы не работает хорошо вместе, потому что если я удали ve реализация __add__ от Sub, это не позволяет мне создать экземпляр. (Я знаю, что __add__ обычно не является свойством, но я хотел использовать «реальный» метод dunder). Такое же ожидаемое поведение возникает, если я удалю реализацию __str__ и foo. Только __name__ ведет себя так.

Что это такое? __name__, что вызывает подобное поведение? Есть ли способ обойти это, или мне нужно, чтобы родительская (абстрактная) реализация вручную подняла для меня TypeError?

ответ

1

Классы имеют атрибут __name__ посредством дескриптораданных на type:

>>> Sub.__name__ 
'Sub' 
>>> '__name__' in Sub.__dict__ 
False 

Это дескриптор данных, поскольку он также перехватывает задания, чтобы гарантировать, что значение является строкой. Фактические значения сохраняются в слот на структуру C, дескриптор представляет собой прокси-сервер для этого значения (так установки нового значения на классе не добавляет новую запись в __dict__ либо):

>>> Sub.__name__ = None 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: can only assign string to NewName.__name__, not 'NoneType' 
>>> Sub.__name__ = 'NewName' 
>>> Sub.__name__ 
'NewName' 
>>> '__name__' in Sub.__dict__ 
False 

(фактически доступ к этому дескриптору без запуска его __get__ на самом деле невозможен, так как type сам не имеет __dict__ и сам имеет __name__).

Это приводит к тому, тесту для атрибута при создании экземпляров Sub для достижения успеха, то класса имеет этот атрибут после всего:

>>> hasattr(Sub, '__name__') 
True 

В случаях Sub, то Base.__name__ реализация затем найдена, так как экземпляр дескриптор правила рассматривают только класс и базовые классы, а не метатип.

+0

@Keozon: проверка, я уверен, потому что есть и дескриптор типа. –

+0

Интересно. Мне придется экспериментировать с этим. Спасибо за информацию! – Keozon