Абстрактные методы в 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.