Является ли принуждение к реализации абстрактного метода непифоническим? - PullRequest
6 голосов
/ 05 июля 2011

При разработке классов абстрактные методы могут быть очень полезны.Из того, что я знаю, Python не имеет механизма для принудительного применения унаследованного класса для реализации абстрактного метода.В моем коде (см. Пример ниже) я ввожу ошибочное утверждение в базовый класс, чтобы вызвать ошибку времени выполнения, если она не реализована.Это непифонично?

class Dog(Animal):
  def speak(self):
   return "bark"

class Animal():
  def speak(self):
   assert(False) #abstract

Ответы [ 4 ]

12 голосов
/ 05 июля 2011

Python на самом деле имеет абстрактные классы с абстрактными методами:

>>> import abc
>>> 
>>> class IFoo(object):
...     __metaclass__ = abc.ABCMeta
...     
...     @abc.abstractmethod
...     def foo(self):
...         pass
... 
>>> foo = IFoo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Cant instantiate abstract class IFoo with abstract methods foo
>>> class FooDerived(IFoo):
...     pass
... 
>>> foo = FooDerived()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Cant instantiate abstract class FooDerived with abstract methods foo
>>> class FooImplements(FooDerived):
...     def foo(self):
...         print "foo'ed"
... 
>>> foo = FooImplements()
>>> foo.foo()
foo'ed
>>> 

С другой стороны, фундаментальный вопрос "Является ли этот питон" немного сложнее сказать.Если ваше намерение состоит в том, чтобы предоставить абстрактный базовый класс, чтобы позже вы могли проверить, что значения наследуют от него, то нет, это не особенно питонно, даже если можно сделать произвольные типы абстрактными подклассами вашего базового класса.С другой стороны, совершенно нормально предоставить абстрактный базовый класс, который реализует некоторые функциональные возможности на основе реализации, предоставленной в конкретных подклассах.Например, collections.Sequence и collections.Mapping делают это только для классов, подобных спискам, и классов, подобных диктовкам;подклассы могут предоставить __getitem__ и могут получить __contains__ и другие бесплатно.

Наверняка вам никогда не следует использовать assert(), кроме как для документирования ожиданий кода;Если на самом деле возможен сбой утверждения, вам не следует использовать утверждение.Оптимизированный питон (python -O script.py) не проверяет утверждения.

Редактировать: дополнительная экспозиция:

Если вы проверяете тип значения:

def foo(bar):
    if not isinstance(bar, AbstractBaz):
        raise ValueError, ("bar must be an instance of AbstractBaz, "
                           "got %s" % type(bar))

Если по какой-то причине вы не можете использовать @abstractmethod, новсе еще хотите этот эффект, вы должны поднять NotImplementedError.Возможно, вы захотите сделать это, потому что вам действительно нужны экземпляры этого класса, некоторые из которых могут не нуждаться в реализации дополнительных функций.Вы все еще должны учитывать возможность того, что функция была вызвана через super().В первом приближении это может выглядеть следующим образом.

class Foo(object):
    def bar(self, baz):
        if self.bar.im_func == Foo.bar.im_func:
            raise NotImplementedError, "Subclasses must implement bar"
5 голосов
/ 05 июля 2011

Азбука является артефактом C ++ и противоречит типу утки.Если класс Animal не определит speak, он будет делать то, что вы намереваетесь, без каких-либо усилий.

>>> class Animal(object):
...     pass
... 
>>> class Dog(Animal):
...     def speak(self):
...             print "bark"
... 
>>> animal = Animal()
>>> dog = Dog()
>>> animal.speak()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Animal' object has no attribute 'speak'
>>> dog.speak()
bark

C ++ и родственные языки вынуждают вас создавать ABC, потому что ABC на самом деле является описанием интерфейса.Python отказывается от деклараций интерфейса, поддерживаемых компилятором, когда они пытаются документировать - в коде - контракт, который лучше реализуется с помощью внелингвистических средств.

2 голосов
/ 05 июля 2011

Что ж, если вы не включите метод speak в свой базовый класс и каким-то образом будете его использовать, код все равно будет неудачным.Вопрос в том, насколько это вероятно и как сообщить пользователю (NotImplementedError может подойти здесь лучше, чем утверждение).

0 голосов
/ 05 июля 2011

В целом, с python не всегда возможно напрямую применить ограничения к коду, по крайней мере, с объектно-ориентированной точки зрения (думая об абстрактных классах, частных методах, ...).Чтобы принудительно реализовать подкласс для реализации метода, вы можете сделать что-то вроде:

class Animal():
  def speak(self):
   raise NotImplementedError #abstract

class Dog(Animal):
  def speak(self):
   return "bark"

class MuteAnimal(Animal):
  pass

Это не означает, что метод будет реализован подклассом, но просто вызовет ошибку, когда говорящийметод не реализован.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...