Python中的“接口”:Yea还是Nay?

时间:2009-02-16 02:15:18

标签: python documentation interface coding-style

所以我在静态土地上花了大量时间后开始使用Python。我已经看到一些项目使得“接口”实际上只是没有任何实现的类。之前,我嘲笑这个想法,忽略那些项目的那一部分。但现在,我开始热衷于这个想法了。

我们很清楚,Python中的界面看起来像这样:

class ISomething(object):
    def some_method():
        pass
    def some_other_method(some_argument):
        pass

请注意,您没有将self传递给任何方法,因此要求重写该方法以进行调用。我认为这是一种很好的文档和完整性测试形式。

那么每个人对这个想法的看法是什么?我已经完成了所有C#编程的洗脑,或者这是一个好主意吗?

11 个答案:

答案 0 :(得分:29)

我不确定这是什么意思。接口(无论如何这种形式)主要是为了解决缺少多重继承问题。但是Python有MI,所以为什么不做一个抽象类?

class Something(object):
    def some_method(self):
        raise NotImplementedError()
    def some_other_method(self, some_argument):
        raise NotImplementedError()

答案 1 :(得分:12)

在Python 2.6及更高版本中,您可以使用abstract base classes代替。这些很有用,因为您可以通过使用“isinstance”来测试某些内容是否实现了给定的ABC。像往常一样,Python中的概念并不像严格的语言那样严格执行,但它很方便。此外,还有很好的惯用方法可以使用装饰器声明抽象方法 - 请参阅上面的链接以获取示例。

答案 2 :(得分:11)

在某些情况下,接口可以非常方便。 Twisted生成fairly extensive useZope interfaces,而在我正在研究Zope接口的项目中效果非常好。最近打包的Enthought的特征添加了interfaces,但我对它们没有任何经验。

请注意过度使用 - 鸭子类型和协议是Python的一个基本方面,只有在绝对必要时才使用接口。

答案 3 :(得分:10)

pythonic方式是“请求宽恕而不是获得许可”。接口 all 关于接收对对象执行某些操作的权限。 Python更喜欢这个:

def quacker(duck):
    try:
        duck.quack():
    except AttributeError:
        raise ThisAintADuckException

答案 4 :(得分:6)

我认为接口不会在代码环境中添加任何内容。

  • 在没有它们的情况下执行方法定义。如果某个对象希望有Foo并且方法为bar(),但它不会,则会抛出AttributeError
  • 只需确保定义接口方法并不能保证其正确性;无论如何,行为单元测试需要到位。
  • 编写一个“读取或读取”页面同样有效,描述了您的对象需要与接口中的内容兼容的方法,因为在接口类中有详细的文档字符串,因为您可能无论如何要去测试它。其中一个测试可以是所有兼容对象的标准,它将检查每个基本方法的调用和返回类型。

答案 5 :(得分:5)

看起来对我来说是不必要的 - 当我写这样的类时,我通常只使用没有方法的基类(你的ISomething),并在实际文档中提到哪些方法子类应该覆盖

答案 6 :(得分:4)

您可以使用动态类型语言创建接口,但在编译时不会强制执行接口。如果您忘记实现(或错误!)接口的方法,静态类型语言的编译器将警告您。由于您在动态类型语言中没有收到此类帮助,因此您的接口声明仅用作文档。 (这不一定是坏事,只是你的接口声明没有提供运行时优势而不是写评论。)

答案 7 :(得分:3)

我打算用我的Python项目做类似的事情,我要添加的唯一内容是:

  • 每个接口和所有抽象方法的超长,深入的文档字符串。
  • 我会添加所有必需的参数,因此有一个确定的列表。
  • 提出异常而不是'通过'。
  • 前缀所有方法,因此它们显然是界面的一部分 - 界面Foo:def foo_method1()

答案 8 :(得分:1)

我个人将接口与Zope组件架构(ZCA)结合使用。优点不是拥有接口,而是能够将它们与适配器和实用程序(单例)一起使用。

E.g。你可以创建一个适配器,它可以采用一个实现ISomething的类,但将它适应于某个接口ISomethingElse。基本上它是一个包装。

原始课程将是:

class MyClass(object):
  implements(ISomething)

  def do_something(self):
      return "foo"

然后想象接口ISomethingElse有一个方法do_something_else()。适配器可能如下所示:

class SomethingElseAdapter(object):
    implements(ISomethingElse)
    adapts(ISomething)

    def __init__(self, context):
        self.context = context

    def do_something_else():
        return self.context.do_something()+"bar"

然后,您将使用组件注册表注册该适配器,然后您可以像这样使用它:

>>> obj = MyClass()
>>> print obj.do_something()
"foo"
>>> adapter = ISomethingElse(obj)
>>> print adapter.do_something_else()
"foobar"

为您提供的功能是能够使用类不直接提供的功能扩展原始类。您可以在不更改该类的情况下执行此操作(它可能位于不同的产品/库中),您只需通过不同的实现交换该适配器,而无需更改使用它的代码。这一切都是通过在初始化时注册组件来完成的。

这当然主要用于框架/库。

我认为需要一些时间来适应它,但我真的不想再没有它了。但正如之前所说的那样,你需要准确地思考它有意义的地方以及它没有的地方。当然,它本身的接口也可以作为API的文档使用。它对于单元测试也很有用,您可以测试您的类是否实际实现了该接口。最后但并非最不重要的是,我喜欢从编写界面和一些doctests开始,以了解我实际要编写的内容。

有关详细信息,您可以查看我的little introduction to it,并且有一个quite extensive description of it's API

答案 9 :(得分:1)

Glyph Lefkowitz(Twisted fame)就在最近wrote an article on this topic。我个人认为不需要接口,而是YMMV。

答案 10 :(得分:1)

你看过PyProtocols?它有一个很好的接口实现,你应该看看。