如何在python中实现接口?

时间:2010-01-23 18:29:07

标签: python oop

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

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

8 个答案:

答案 0 :(得分:104)

正如其他人所说:

Python中不需要接口。这是因为Python具有适当的多重继承,并且还具有类型,这意味着必须在Java中具有接口的位置,您不必在Python中使用它们。

也就是说,接口仍有多种用途。其中一些被Python 2.6中引入的Pythons Abstract Base Classes所涵盖。如果您想创建无法实例化的基类,但提供特定的接口或实现的一部分,它们很有用。

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

答案 1 :(得分:49)

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

答案 2 :(得分:26)

Python的接口有第三方实现(最流行的是Zope's,也用于Twisted),但更常见的是Python编码器更喜欢使用更丰富的概念,称为“抽象基础”类“(ABC),它结合了一个接口,并有可能在那里有一些实现方面。在Python 2.6及更高版本中,ABCs得到了特别好的支持,请参阅the PEP,但即使在早期版本的Python中,它们通常被视为“可行的方式” - 只需定义一个类,其中一些方法会引发{{} 1}}以便子类会注意到他们最好覆盖那些方法! - )

答案 3 :(得分:17)

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

class IInterface:
    def show(self): raise NotImplementedError

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

答案 4 :(得分:14)

interface支持Python 2.7和Python 3.4 +。

您必须进入install界面

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

答案 5 :(得分:12)

在现代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()从插件目录加载所有类,并且它们是基类的子类,则可以通过{{1}直接访问它们},您可以确保在实例化过程中对所有接口强制执行接口协定。

以下是上述__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

(受this PyMOTW-3 post启发。)

答案 6 :(得分:12)

我邀请您以 Structural subtyping (static duck typing) (PEP 544)

的形式探索 Python 3.8 为主题提供的内容

查看简短说明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

答案 7 :(得分:7)

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

E.g。如果你有观察者和可观察者,observable对订阅支持IObserver接口的对象感兴趣,而IObserver接口又有notify动作。这在编译时检查。

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

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