Как мне реализовать интерфейсы в Python? - PullRequest
119 голосов
/ 23 января 2010
public interface IInterface
{
    void show();
}

 public class MyClass : IInterface
{

    #region IInterface Members

    public void show()
    {
        Console.WriteLine("Hello World!");
    }

    #endregion
}

Как реализовать Python-эквивалент этого кода на C #?

class IInterface(object):
    def __init__(self):
        pass

    def show(self):
        raise Exception("NotImplementedException")


class MyClass(IInterface):
   def __init__(self):
       IInterface.__init__(self)

   def show(self):
       print 'Hello World!'

Это хорошая идея? Пожалуйста, приведите примеры в своих ответах.

Ответы [ 7 ]

107 голосов
/ 23 января 2010

Как уже упоминалось другими здесь:

Интерфейсы не нужны в Python. Это потому, что Python имеет правильное множественное наследование, а также ducktyping, что означает, что в местах, где вы должны иметь интерфейсы в Java, вы не должны иметь их в Python.

Тем не менее, есть еще несколько вариантов использования интерфейсов. Некоторые из них охватываются абстрактными базовыми классами Pythons, представленными в Python 2.6. Они полезны, если вы хотите создать базовые классы, которые не могут быть созданы, но предоставляют определенный интерфейс или часть реализации.

Другое использование, если вы как-то хотите указать, что объект реализует определенный интерфейс, и вы можете использовать для этого и ABC, используя их подклассы. Другой способ - это zope.interface, модуль, который является частью архитектуры компонентов Zope, действительно классная структура компонентов. Здесь вы не наследуете от интерфейсов, а вместо этого помечаете классы (или даже экземпляры) как реализующие интерфейс. Это также можно использовать для поиска компонентов в реестре компонентов. Supercool!

49 голосов
/ 17 апреля 2014

Использование модуля abc для абстрактных базовых классов, похоже, помогает.

from abc import ABCMeta, abstractmethod

class IInterface:
    __metaclass__ = ABCMeta

    @classmethod
    def version(self): return "1.0"
    @abstractmethod
    def show(self): raise NotImplementedError

class MyServer(IInterface):
    def show(self):
        print 'Hello, World 2!'

class MyBadServer(object):
    def show(self):
        print 'Damn you, world!'


class MyClient(object):

    def __init__(self, server):
        if not isinstance(server, IInterface): raise Exception('Bad interface')
        if not IInterface.version() == '1.0': raise Exception('Bad revision')

        self._server = server


    def client_show(self):
        self._server.show()


# This call will fail with an exception
try:
    x = MyClient(MyBadServer)
except Exception as exc:
    print 'Failed as it should!'

# This will pass with glory
MyClient(MyServer()).client_show()
26 голосов
/ 23 января 2010

Существуют сторонние реализации интерфейсов для Python (наиболее популярны Zope's , также используемые в Twisted ), но чаще всего программисты Python предпочитают использовать более богатую концепцию, известную как «Абстрактный базовый класс» (ABC), который сочетает в себе интерфейс с возможностью иметь некоторые аспекты реализации. ABC особенно хорошо поддерживаются в Python 2.6 и более поздних версиях, см. PEP , но даже в более ранних версиях Python их обычно рассматривают как "путь" - просто определите класс, некоторые из методов которого поднять NotImplementedError, чтобы подклассы были замечены, что им лучше переопределить эти методы! -)

18 голосов
/ 23 января 2010

Примерно так (может не работать, так как у меня нет Python):

class IInterface:
    def show(self): raise NotImplementedError

class MyClass(IInterface):
    def show(self): print "Hello World!"
17 голосов
/ 24 октября 2018

интерфейс поддерживает Python 2.7 и Python 3.4 +.

Чтобы установить интерфейс, вам нужно

pip install python-interface

Пример кода:

from interface import implements, Interface

class MyInterface(Interface):

    def method1(self, x):
        pass

    def method2(self, x, y):
        pass


class MyClass(implements(MyInterface)):

    def method1(self, x):
        return x * 2

    def method2(self, x, y):
        return x + y
13 голосов
/ 10 июля 2018

Реализация интерфейсов с абстрактными базовыми классами намного проще в современном Python 3, и они служат в качестве контракта интерфейса для расширений плагинов.

Создание интерфейса / абстрактного базового класса:

from abc import ABC, abstractmethod

class AccountingSystem(ABC):

    @abstractmethod
    def create_purchase_invoice(self, purchase):
        pass

    @abstractmethod
    def create_sale_invoice(self, sale):
        log.debug('Creating sale invoice', sale)

Создайте нормальный подкласс и переопределите все абстрактные методы:

class GizmoAccountingSystem(AccountingSystem):

    def create_purchase_invoice(self, purchase):
        submit_to_gizmo_purchase_service(purchase)

    def create_sale_invoice(self, sale):
        super().create_sale_invoice(sale)
        submit_to_gizmo_sale_service(sale)

При желании вы можете иметь общую реализацию в абстрактных методах, как в create_sale_invoice(), вызывая ее явно с super() в подклассе, как указано выше.

Ошибка создания подкласса, который не реализует все абстрактные методы:

class IncompleteAccountingSystem(AccountingSystem):
    pass

>>> accounting = IncompleteAccountingSystem()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class IncompleteAccountingSystem with abstract methods
create_purchase_invoice, create_sale_invoice

Вы также можете иметь абстрактные свойства, статические и методы классов, комбинируя соответствующие аннотации с @abstractmethod.

Абстрактные базовые классы отлично подходят для реализации систем на основе плагинов. Все импортированные подклассы класса доступны через __subclasses__(), поэтому, если вы загружаете все классы из каталога плагинов с помощью importlib.import_module() и если они подклассируют базовый класс, у вас есть прямой доступ к ним через __subclasses__() и вы можете быть уверены что интерфейсный контракт применяется для всех из них во время реализации.

Вот реализация загрузки плагинов для AccountingSystem примера выше:

...
from importlib import import_module

class AccountingSystem(ABC):

    ...
    _instance = None

    @classmethod
    def instance(cls):
        if not cls._instance:
            module_name = settings.ACCOUNTING_SYSTEM_MODULE_NAME
            import_module(module_name)
            subclasses = cls.__subclasses__()
            if len(subclasses) > 1:
                raise InvalidAccountingSystemError('More than one '
                        f'accounting module: {subclasses}')
            if not subclasses or module_name not in str(subclasses[0]):
                raise InvalidAccountingSystemError('Accounting module '
                        f'{module_name} does not exist or does not '
                        'subclass AccountingSystem')
            cls._instance = subclasses[0]()
        return cls._instance

Затем вы можете получить доступ к объекту плагина системы учета через класс AccountingSystem:

>>> accountingsystem = AccountingSystem.instance()

(Вдохновлено этим сообщением PyMOTW-3 .)

7 голосов
/ 23 января 2010

Насколько я понимаю, интерфейсы не нужны в динамических языках, таких как Python. В Java (или C ++ с его абстрактным базовым классом) интерфейсы являются средством, обеспечивающим, например, Вы передаете правильный параметр, способный выполнять множество задач.

например. если у вас есть наблюдатель и наблюдаемый, то наблюдаемый заинтересован в подписке объектов, поддерживающих интерфейс IObserver, который, в свою очередь, имеет действие notify. Это проверяется во время компиляции.

В Python нет такой вещи, как compile time, и поиск метода выполняется во время выполнения. Кроме того, можно переопределить поиск с помощью магических методов __getattr __ () или __getattribute __ (). Другими словами, вы можете передать в качестве наблюдателя любой объект, который может вернуть вызываемый при доступе к атрибуту notify.

Это приводит меня к выводу, что интерфейсы в Python существуют - просто их применение откладывается до момента, когда они фактически используются

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