Абстрактные методы в Python
У меня возникли проблемы с использованием наследования с Python. Хотя концепция кажется мне слишком простой для Java, но до сих пор я не мог понять в Python, что, по крайней мере, удивительно для меня.
У меня есть прототип, который следует:
class Shape():
def __init__(self, shape_name):
self.shape = shape_name
class Rectangle(Shape):
def __init__(self, name):
self.shape = name
В приведенном выше коде, как я могу сделать абстрактный метод, который должен быть реализован для всех подклассов?
Ответы
Ответ 1
Что-то в этих строках, используя ABC
import abc
class Shape(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def method_to_implement(self, input):
"""Method documentation"""
return
Также прочитайте этот хороший учебник: http://www.doughellmann.com/PyMOTW/abc/
Вы также можете проверить zope.interface, который использовался до введения ABC в python.
Ответ 2
Прежде чем вводить abc, вы увидите это часто.
class Base(object):
def go(self):
raise NotImplementedError("Please Implement this method")
class Specialized(Base):
def go(self):
print "Consider me implemented"
Ответ 3
Смотрите модуль abc. В принципе, вы определяете __metaclass__ = abc.ABCMeta
в классе, а затем украшаете каждый абстрактный метод с помощью @abc.abstractmethod
. Классы, полученные из этого класса, не могут быть затем созданы, если только все абстрактные методы не были отменены.
Если ваш класс уже использует метакласс, выведите его из ABCMeta
, а не type
, и вы можете продолжать использовать свой собственный метакласс.
Была предложена дешевая альтернатива (и наилучшая практика перед модулем abc
) заключалась бы в том, чтобы все ваши абстрактные методы просто вызывали исключение (NotImplementedError
является хорошим), так что классы, полученные из него, должны были бы переопределите этот метод.
Однако решение abc
лучше, потому что оно не позволяет экземплярам вообще создавать экземпляр (т.е. он "не работает быстрее" ), а также потому, что вы можете предоставить стандартную или базовую реализацию каждого метода, который может быть достигнут используя функцию super()
в производных классах.
Ответ 4
Вы не можете, с языковыми примитивами. Как уже было сказано, abc package предоставляет эту функцию в Python 2.6 и более поздних версиях, но нет вариантов для Python 2.5 и ранее. Пакет abc
не является новой функцией Python; вместо этого он добавляет функциональность, добавляя явное "говорит ли этот класс, что он это делает?" проверяет с проверками согласованности вручную, чтобы вызвать ошибку при инициализации, если такие объявления сделаны ложно.
Python - это воинственно динамически типизированный язык. Он не указывает языковые примитивы, чтобы вы могли запретить компиляцию программы, поскольку объект не соответствует требованиям типа; это можно обнаружить только во время выполнения. Если вам требуется, чтобы подкласс реализовал метод, документируйте это, а затем просто вызовите метод в слепой надежде, что он будет там.
Если он там, фантастический, он просто работает; это называется утиная печать, и ваш объект достаточно смещен, как утка, чтобы удовлетворить интерфейс. Это работает отлично, даже если self
- объект, к которому вы вызываете такой метод, для целей обязательных переопределений из-за базовых методов, которые нуждаются в конкретных реализациях функций (общих функций), поскольку self
является условным, ничего особенного.
Исключение составляет __init__
, потому что, когда вы вызываете ваш инициализатор, инициализатор производного типа не имеет, поэтому он еще не имел возможности сшивать свои собственные методы на объект.
Если бы метод не был реализован, вы получите AttributeError (если он вообще не существует) или TypeError (если что-то под этим именем есть, но это не функция или у нее не было этой подписи), Это зависит от вас, как вы справляетесь с этим: либо называть его ошибкой программиста, либо разрешать ему сбой программы (и она должна "быть очевидной для разработчика python, что вызывает такую ошибку там - неудовлетворенный интерфейс утки" ), либо улавливать и обрабатывать эти исключения, когда вы обнаружите, что ваш объект не поддерживает то, что вы пожелаете. Обход AttributeError и TypeError очень важен во многих ситуациях.
Ответ 5
Абстрактные базовые классы - это глубокая магия. Периодически я реализую что-то, используя их, и удивляюсь своей собственной хитрости, очень скоро после этого я полностью смущен своей собственной умностью (это может быть просто личным ограничением).
Другой способ сделать это (должен быть в python std libs, если вы спросите меня) - сделать декоратор.
def abstractmethod(method):
"""
An @abstractmethod member fn decorator.
(put this in some library somewhere for reuse).
"""
def default_abstract_method(*args, **kwargs):
raise NotImplementedError('call to abstract method '
+ repr(method))
default_abstract_method.__name__ = method.__name__
return default_abstract_method
class Shape(object):
def __init__(self, shape_name):
self.shape = shape_name
@abstractmethod
def foo(self):
print "bar"
return
class Rectangle(Shape):
# note you don't need to do the constructor twice either
pass
r = Rectangle("x")
r.foo()
Я не писал декоратора. Мне просто пришло в голову, что у кого-то будет. Вы можете найти его здесь: http://code.activestate.com/recipes/577666-abstract-method-decorator/ Хороший jimmy2times. Обратите внимание на обсуждение в нижней части этой страницы r.e. типа безопасности декоратора. (Это может быть исправлено с помощью модуля проверки, если кто-либо был так склонен).
Ответ 6
Вы можете использовать шесть и abc для эффективного создания класса для python2 и python3 следующим образом:
import six
import abc
@six.add_metaclass(abc.ABCMeta)
class MyClass(object):
"""
documentation
"""
@abc.abstractmethod
def initialize(self, para=None):
"""
documentation
"""
raise NotImplementedError
Это - это документ awesomoe.
Ответ 7
Вы должны реализовать абстрактный базовый класс (ABC).
Проверить документы python