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

这是个好主意吗?请在回答中举例。


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


类似这样的东西(可能不起作用,因为我周围没有Python):

class IInterface:
    def show(self): raise NotImplementedError

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

我的理解是,在Python这样的动态语言中,接口并不是那么必要。在Java(或具有抽象基类的c++)中,接口是确保传递正确参数,能够执行一组任务的手段。

例如,如果你有观察者和可观察对象,可观察对象对订阅支持IObserver接口的对象感兴趣,而IObserver接口又具有通知操作。在编译时进行检查。

在Python中,没有编译时这样的东西,方法查找是在运行时执行的。此外,可以使用__getattr__()或__getattribute__()魔术方法覆盖查找。换句话说,你可以传递,作为观察者,任何对象,可以返回可调用的访问notify属性。

这让我得出结论,Python中的接口确实存在——只是它们的实施被推迟到了实际使用的时候


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

接口在Python中不是必需的。这是因为Python有适当的多重继承,还有鸭子类型,这意味着在Java中必须有接口的地方,在Python中不必有它们。

也就是说,接口仍然有多种用途。其中一些由Python 2.6中引入的Python抽象基类覆盖。如果你想让基类不能被实例化,但是提供了一个特定的接口或者实现的一部分,它们是很有用的。

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


对抽象基类使用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()

在现代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()

(灵感来自这篇pymotw3帖子。)


接口支持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

我邀请您探索Python 3.8以结构子类型(静态鸭子类型)(PEP 544)的形式为主题提供了什么。

参见简短说明https://docs.python.org/3/library/typing.html#typing.Protocol

下面这个简单的例子是这样的:

from typing import Protocol

class MyShowProto(Protocol):
    def show(self):
        ...


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


class MyOtherClass:
    pass


def foo(o: MyShowProto):
    return o.show()

foo(MyClass())  # ok
foo(MyOtherClass())  # fails

foo(MyOtherClass())将失败的静态类型检查:

$ mypy proto-experiment.py 
proto-experiment.py:21: error: Argument 1 to "foo" has incompatible type "MyOtherClass"; expected "MyShowProto"
Found 1 error in 1 file (checked 1 source file)

此外,你可以显式地指定基类,例如:

class MyOtherClass(MyShowProto):

但请注意,这使得基类的方法实际上在子类上可用,因此静态检查器将不会报告MyOtherClass上缺少方法定义。 因此,在这种情况下,为了获得有用的类型检查,我们希望显式实现的所有方法都应该使用@abstractmethod来装饰:

from typing import Protocol
from abc import abstractmethod

class MyShowProto(Protocol):
    @abstractmethod
    def show(self): raise NotImplementedError


class MyOtherClass(MyShowProto):
    pass


MyOtherClass()  # error in type checker