如何在python中实现接口?

Pra*_*are 147 python oop

public interface IInterface
{
    void show();
}

 public class MyClass : IInterface
{

    #region IInterface Members

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

    #endregion
}
Run Code Online (Sandbox Code Playgroud)

如何实现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!'
Run Code Online (Sandbox Code Playgroud)

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

Len*_*bro 125

正如其他人所说:

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

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

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

  • “这是因为Python有适当的多重继承”,谁说接口就是为了多重继承? (48认同)
  • 你能详细说明一下吗?1.如何实现这样的界面?2.如何使用它来查找组件? (10认同)
  • “接口在Python中不是必需的。除非是必要的。” (10认同)
  • 人们在多重继承、内存占用方面谈论接口:如果我有一个抽象文件系统的模块怎么办?后端可以是本地文件系统、RAM 或众多云存储系统之一。从这样的模块实例化一个类并知道该对象将具有某些方法,无论后端实现如何,都会很好。IOW,接口。与多重继承无关,与内存无关。 (9认同)
  • 一个例子将大大改善这个答案。 (8认同)
  • 我在上面的回答中做到了。 (4认同)
  • 我有一种感觉,大多数 python 开发人员没有看到接口的必要性,因为简单来说,他们没有使用过接口,因此看到了接口提供的实用程序。 (3认同)
  • @LennartRegebro:我很确定 Java(和 C#)没有多重继承,而且也没有假装它们有。继承与“是”关系是两件不同的事情。继续实现接口或从类继承也是两件不同的事情。 (3认同)
  • 接口通常用于在传递对象时具有可预测的结果/强制成员正确性。如果python支持此选项,那就太好了。它还将使开发工具具有更好的智能感知能力 (2认同)

小智 57

使用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()
Run Code Online (Sandbox Code Playgroud)

  • @MikedeKlerk,abc 模块确实内置于 python 中。设置其中一些模式需要做更多的工作,因为由于替代模式被认为“更Pythonic”,Python 中基本上不需要它们。对于绝大多数开发人员来说,正如您所说,“根本不使用”。然而,Python 创建者承认有一些非常特殊的情况确实需要这些接口功能,因此提供了一个易于使用的 API 来实现这一点。 (9认同)
  • Yugghh,需要一些模块来作为语言本身的一部分,或者根本不使用IMO。 (6认同)
  • @MikedeKlerk 我完全同意。就像 Python 对打字的回答一样;我不必导入模块来声明我希望类型成为类型。对此的回应通常是“Python 是动态类型的”,但这不是借口。Java + Groovy 解决了这个问题。Java 用于静态内容,Groovy 用于动态内容。 (4认同)
  • 你的意思是:`如果不是 server.version() == '1.0': raise ...`?我真的不明白这条线。欢迎解释。 (2认同)

Ale*_*lli 29

Python的接口有第三方实现(最流行的是Zope,也用于Twisted),但更常见的是Python编码器更喜欢使用更丰富的概念,称为"抽象基类"(ABC),它将接口与也有可能在那里有一些实施方面.ABCs在Python 2.6及更高版本中得到了特别好的支持,请参阅PEP,但即使在Python的早期版本中,它们通常被视为"要走的路" - 只需定义一个类,其中某些方法会引发,NotImplementedError以便子类将成为注意他们最好覆盖那些方法! - )

  • *Python的接口有第三方实现*这是什么意思?你能解释一下ABC吗? (3认同)
  • 好吧,我会质疑ABC的"更富有".;)zope.interface可以做的事情,ABC不能和其他方式一样.但是否则你和往常一样.+1 (2认同)

blu*_*ray 28

接口支持Python 2.7和Python 3.4+.

安装必须的接口

pip install python-interface
Run Code Online (Sandbox Code Playgroud)

示例代码:

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
Run Code Online (Sandbox Code Playgroud)

  • 恕我直言,此库的主要优点是它为您带来了早期失败:如果您的类未正确实现指定的接口,则在读入该类后会立即获得异常-您甚至不必使用它。使用Python自己的抽象基类,您在首次实例化类时会遇到异常,这可能要晚得多。 (5认同)
  • 这看起来很简单. (4认同)
  • 这是不必要的,ABC 提供了类似的内置功能。 (4认同)
  • @DanielCasares ABC 是否提供了实际的接口,或者您的意思是没有状态或实现的抽象类是 ABC 提供的解决方案? (2认同)

Pet*_*olf 22

我邀请您以(PEP 544) 的形式探索 Python 3.8 为主题提供的内容Structural subtyping (static duck typing)

请参阅简短说明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
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

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

class MyOtherClass(MyShowProto):
Run Code Online (Sandbox Code Playgroud)

但请注意,这使得基类的方法在子类上实际可用,因此静态检查器不会报告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
Run Code Online (Sandbox Code Playgroud)

  • 感谢您展示新功能。 (4认同)
  • @JoeyBaruch正如`abc`包的文档中所写(参见https://docs.python.org/3/library/abc.html#abc.abstractmethod),您可以将`@staticmethod`与`@abstractmethod`结合起来以获得所需的效果(装饰器“@abstractmethod”应放置在最内层)。 (2认同)

Ban*_*i-T 20

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

class IInterface:
    def show(self): raise NotImplementedError

class MyClass(IInterface):
    def show(self): print "Hello World!"
Run Code Online (Sandbox Code Playgroud)

  • 关于`__init __(self)`构造函数我该怎么办? (2认同)
  • 这就是为什么 `abc.ABC` 比引发 `NotImplementedError` 好得多 - 没有实现所有抽象方法的 `abc.ABC` 子类的实例化会提前失败,因此您可以避免错误。有关错误的外观,请参阅下面的答案。 (2认同)

mrt*_*rts 16

在现代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)
Run Code Online (Sandbox Code Playgroud)

创建一个普通的子类并覆盖所有抽象方法:

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)
Run Code Online (Sandbox Code Playgroud)

您可以选择在抽象方法中使用通用实现,如上所述在子类中显式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
Run Code Online (Sandbox Code Playgroud)

您还可以通过组合相应的注释来使用抽象属性,静态和类方法@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
Run Code Online (Sandbox Code Playgroud)

然后,您可以通过AccountingSystem类访问会计系统插件对象:

>>> accountingsystem = AccountingSystem.instance()
Run Code Online (Sandbox Code Playgroud)

(灵感来自这篇PyMOTW-3帖子.)

  • **问题:** 模块名称“ABC”代表什么? (5认同)
  • “ABC”代表“抽象基类”,请参见[官方文档](https://docs.python.org/3/library/abc.html) (2认同)

Tom*_*ski 7

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

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

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

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