2016-12-15 12 views
3

PEP 3119 утверждает, что:Почему @abstractmethod необходимо использовать в классе, чей метакласс получен из ABCMeta?

@abstractmethod декоратора следует использовать только внутри тела класса, и только для классов, метакласс (полученный из) ABCMeta. Динамическое добавление абстрактных методов в класс или попытка изменить статус абстракции метода или класса после его создания не поддерживаются.

Я не могу найти, однако, объяснение, почему это так. В частности, я не замечаю разницы в поведении при использовании только @abstractmethod в классе, который явно не наследуется от ABCMeta. В следующем простом примере, если я правильно понимаю, правильный способ делать вещи будет:

import six 
from abc import ABCMeta 
from abc import abstractmethod 

class Base(six.with_metaclass(ABCMeta)): 
    def __init__(self): 
     print('Init abstract base') 

    @abstractmethod 
    def do_something(self): 
     pass 

class Subclass(Base): 
    def __init__(self): 
     super(Subclass, self).__init__() 

    def do_something(self): 
     print('Done.') 

sub = Subclass() 
sub.do_something() 

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

from abc import abstractmethod 

class Base(object): 
    def __init__(self): 
     print('Init abstract base') 

    @abstractmethod 
    def do_something(self): 
     pass 

class Subclass(Base): 
    def __init__(self): 
     super(Subclass, self).__init__() 

    def do_something(self): 
     print('Done.') 

sub = Subclass() 
sub.do_something() 

Я нашел это место даже на более сложных архитектур, поэтому я задаюсь вопросом: когда же последний способ потерпеть неудачу?

ответ

1

Вы не видите никакой разницы, потому что ваш первый подкласс действительно реализует абстрактный метод do_something.

Закомментируйте определения do_something в подклассов в обеих версиях и вы увидите, что в первом случае вы получаете TypeError при попытке создать экземпляр подкласса - вы также получите один пытается создать экземпляр первой версии Base класс сам FWIW. Со второй версией вы можете инициировать оба класса (что не должно быть возможным, поскольку они абстрактны) и называть абстрактным методом do_something - какой вид побеждает один из основных пунктов ABC.

Вы также пропустите немало интересных особенностей ABCs FWIW ...