如何在python中实现接口?

问题:如何在python中实现接口?

public interface IInterface
{
    void show();
}

 public class MyClass : IInterface
{

    #region IInterface Members

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

    #endregion
}

如何实现与C#代码等效的Python?

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!'

这是一个好主意吗??请在您的答案中举例说明。

public interface IInterface
{
    void show();
}

 public class MyClass : IInterface
{

    #region IInterface Members

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

    #endregion
}

How do I implement Python equivalent of this C# code ?

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!'

Is this a good idea?? Please give examples in your answers.


回答 0

正如其他人在这里提到的:

在Python中不需要接口。这是因为Python具有适当的多重继承,还具有鸭式输入法,这意味着必须在Java中具有接口的地方,而不必在Python中具有接口。

也就是说,接口还有多种用途。其中一些被Python 2.6中引入的Pythons抽象基类覆盖。如果要创建无法实例化但提供特定接口或实现的一部分的基类,则它们很有用。

另一种用法是,如果您以某种方式想要指定一个对象实现特定的接口,并且可以通过从它们的子类中使用ABC来实现。另一种方法是zope.interface,它是Zope组件体系结构(一个非常酷的组件框架)的一部分的模块。在这里,您不是从接口子类化,而是将类(甚至实例)标记为实现接口。这也可以用于从组件注册表中查找组件。超酷!

As mentioned by other here:

Interfaces are not necessary in Python. This is because Python has proper multiple inheritance, and also ducktyping, which means that the places where you must have interfaces in Java, you don’t have to have them in Python.

That said, there are still several uses for interfaces. Some of them are covered by Pythons Abstract Base Classes, introduced in Python 2.6. They are useful, if you want to make base classes that cannot be instantiated, but provide a specific interface or part of an implementation.

Another usage is if you somehow want to specify that an object implements a specific interface, and you can use ABC’s for that too by subclassing from them. Another way is zope.interface, a module that is a part of the Zope Component Architecture, a really awesomely cool component framework. Here you don’t subclass from the interfaces, but instead mark classes (or even instances) as implementing an interface. This can also be used to look up components from a component registry. Supercool!


回答 1

将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()

Using the abc module for abstract base classes seems to do the trick.

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()

回答 2

介面支援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

interface supports Python 2.7 and Python 3.4+.

To install interface you have to

pip install python-interface

Example Code:

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

回答 3

在现代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帖子启发。)

Implementing interfaces with abstract base classes is much simpler in modern Python 3 and they serve a purpose as an interface contract for plug-in extensions.

Create the interface/abstract base class:

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)

Create a normal subclass and override all abstract methods:

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)

You can optionally have common implementation in the abstract methods as in create_sale_invoice(), calling it with super() explicitly in the subclass as above.

Instantiation of a subclass that does not implement all the abstract methods fails:

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

You can also have abstract properties, static and class methods by combining corresponding annotations with @abstractmethod.

Abstract base classes are great for implementing plugin-based systems. All imported subclasses of a class are accessible via __subclasses__(), so if you load all classes from a plugin directory with importlib.import_module() and if they subclass the base class, you have direct access to them via __subclasses__() and you can be sure that the interface contract is enforced for all of them during instantiation.

Here’s the plugin loading implementation for the AccountingSystem example above:

...
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

Then you can access the accounting system plugin object through the AccountingSystem class:

>>> accountingsystem = AccountingSystem.instance()

(Inspired by this PyMOTW-3 post.)


回答 4

有用于Python的第三方接口实现(最受欢迎的是Zope’s,也用于Twisted中),但是更常见的是,Python编码人员更喜欢使用称为“抽象基类”(ABC)的更丰富的概念,该概念将接口与那里也有一些实施方面的可能性。在2.6和更高版本的Python 中,特别好地支持ABC,请参阅PEP,但即使在早期版本的Python中,它们也通常被视为“前进的道路”-只需定义一个类,某些类的方法将引发,NotImplementedError这样子类将成为注意,他们最好重写这些方法!-)

There are third-party implementations of interfaces for Python (most popular is Zope’s, also used in Twisted), but more commonly Python coders prefer to use the richer concept known as an “Abstract Base Class” (ABC), which combines an interface with the possibility of having some implementation aspects there too. ABCs are particularly well supported in Python 2.6 and later, see the PEP, but even in earlier versions of Python they’re normally seen as “the way to go” — just define a class some of whose methods raise NotImplementedError so that subclasses will be on notice that they’d better override those methods!-)


回答 5

像这样的东西(可能无法正常工作,因为我没有Python):

class IInterface:
    def show(self): raise NotImplementedError

class MyClass(IInterface):
    def show(self): print "Hello World!"

Something like this (might not work as I don’t have Python around):

class IInterface:
    def show(self): raise NotImplementedError

class MyClass(IInterface):
    def show(self): print "Hello World!"

回答 6

我的理解是,在像Python这样的动态语言中,接口不是必需的。在Java(或带有抽象基类的C ++)中,接口是用于确保例如传递正确的参数,能够执行一组任务的方法。

例如,如果您具有观察者并且是可观察的,那么可观察的对象就是订阅支持IObserver接口的对象,而该对象又会起作用notify。这是在编译时检查的。

在Python中,没有这样的事情,compile time并且在运行时执行方法查找。而且,可以使用__getattr __()或__getattribute __()魔术方法覆盖查找。换句话说,您可以作为观察者传递可以在访问notify属性时返回可调用对象的任何对象。

这使我得出结论,Python中的接口确实存在 -只是它们的执行被推迟到实际使用它们的那一刻

My understanding is that interfaces are not that necessary in dynamic languages like Python. In Java (or C++ with its abstract base class) interfaces are means for ensuring that e.g. you’re passing the right parameter, able to perform set of tasks.

E.g. if you have observer and observable, observable is interested in subscribing objects that supports IObserver interface, which in turn has notify action. This is checked at compile time.

In Python, there is no such thing as compile time and method lookups are performed at runtime. Moreover, one can override lookup with __getattr__() or __getattribute__() magic methods. In other words, you can pass, as observer, any object that can return callable on accessing notify attribute.

This leads me to the conclusion, that interfaces in Python do exist – it’s just their enforcement is postponed to the moment in which they are actually used