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

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


当前回答

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

在现代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中不是必需的。这是因为Python有适当的多重继承,还有鸭子类型,这意味着在Java中必须有接口的地方,在Python中不必有它们。

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

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

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

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

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

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