借助zope.interface深入了解Python接口

开发 后端
Zope.interface 可以帮助声明存在哪些接口,是由哪些对象提供的,以及如何查询这些信息。

[[311934]]

Zope.interface 可以帮助声明存在哪些接口,是由哪些对象提供的,以及如何查询这些信息。

zope.interface 库可以克服 Python 接口设计中的歧义性。让我们来研究一下。

隐式接口不是 Python 之禅

Python 之禅 很宽松,但是有点自相矛盾,以至于你可以用它来例证任何东西。让我们来思考其中最著名的原则之一:“显示胜于隐式”。

传统上,在 Python 中会隐含的一件事是预期的接口。比如函数已经记录了它期望一个“类文件对象”或“序列”。但是什么是类文件对象呢?它支持 .writelines吗?.seek 呢?什么是一个“序列”?是否支持步进切片,例如 a[1:10:2]

最初,Python 的答案是所谓的“鸭子类型”,取自短语“如果它像鸭子一样行走,像鸭子一样嘎嘎叫,那么它可能就是鸭子”。换句话说,“试试看”,这可能是你能得到的最具隐式的表达。

为了使这些内容显式地表达出来,你需要一种方法来表达期望的接口。Zope Web 框架是最早用 Python 编写的大型系统之一,它迫切需要这些东西来使代码明确呈现出来,例如,期望从“类似用户的对象”获得什么。

zope.interface 由 Zope 开发,但作为单独的 Python 包发布。Zope.interface 可以帮助声明存在哪些接口,是由哪些对象提供的,以及如何查询这些信息。

想象编写一个简单的 2D 游戏,它需要各种东西来支持精灵界面(LCTT 译注:“精灵 Sprite”是指游戏面板中各个组件)。例如,表示一个边界框,但也要表示对象何时与一个框相交。与一些其他语言不同,在 Python 中,将属性访问作为公共接口一部分是一种常见的做法,而不是实现 getter 和 setter。边界框应该是一个属性,而不是一个方法。

呈现精灵列表的方法可能类似于:

  1. def render_sprites(render_surface, sprites):
  2.     """
  3.     sprites 应该是符合 Sprite 接口的对象列表:
  4.     * 一个名为 "bounding_box" 的属性,包含了边界框
  5.     * 一个名为 "intersects" 的方法,它接受一个边界框并返回 True 或 False
  6.     """
  7.     pass # 一些做实际渲染的代码

该游戏将具有许多处理精灵的函数。在每个函数中,你都必须在随附文档中指定预期。

此外,某些函数可能期望使用更复杂的精灵对象,例如具有 Z 序的对象。我们必须跟踪哪些方法需要 Sprite 对象,哪些方法需要 SpriteWithZ 对象。

如果能够使精灵是显式而直观的,这样方法就可以声明“我需要一个精灵”,并有个严格定义的接口,这不是很好吗?来看看 zope.interface

  1. from zope import interface
  2.  
  3. class ISprite(interface.Interface):
  4.  
  5.     bounding_box = interface.Attribute(
  6.         "边界框"
  7.     )
  8.  
  9.     def intersects(box):
  10.         "它和一个框相交吗?"

乍看起来,这段代码有点奇怪。这些方法不包括 self,而包含 self 是一种常见的做法,并且它有一个属性。这是在 zope.interface 中声明接口的方法。这看起来很奇怪,因为大多数人不习惯严格声明接口。

这样做的原因是接口显示了如何调用方法,而不是如何定义方法。因为接口不是超类,所以它们可以用来声明数据属性。

下面是一个能带有圆形精灵的接口的一个实现:

  1. @implementer(ISprite)
  2. @attr.s(auto_attribs=True)
  3. class CircleSprite:
  4.     x: float
  5.     y: float
  6.     radius: float
  7.  
  8.     @property
  9.     def bounding_box(self):
  10.         return (
  11.             self.x - self.radius,
  12.             self.y - self.radius,
  13.             self.x + self.radius,
  14.             self.y + self.radius,
  15.         )
  16.  
  17.     def intersects(self, box):
  18.         # 当且仅当至少一个角在圆内时,方框与圆相交
  19.         top_left, bottom_right = box[:2], box[2:]
  20.         for choose_x_from (top_left, bottom_right):
  21.             for choose_y_from (top_left, bottom_right):
  22.                 x = choose_x_from[0]
  23.                 y = choose_y_from[1]
  24.                 if (((x - self.x) ` 2 + (y - self.y) ` 2) <=
  25.                     self.radius ` 2):
  26.                      return True
  27.         return False

显式声明了实现了该接口的 CircleSprite 类。它甚至能让我们验证该类是否正确实现了接口:

  1. from zope.interface import verify
  2.  
  3. def test_implementation():
  4.     sprite = CircleSprite(x=0, y=0, radius=1)
  5.     verify.verifyObject(ISprite, sprite)

这可以由 pytest、nose 或其他测试框架运行,它将验证创建的精灵是否符合接口。测试通常是局部的:它不会测试仅在文档中提及的内容,甚至不会测试方法是否可以在没有异常的情况下被调用!但是,它会检查是否存在正确的方法和属性。这是对单元测试套件一个很好的补充,至少可以防止简单的拼写错误通过测试。 

 

责任编辑:庞桂玉 来源: Linux中国
相关推荐

2010-11-19 16:22:14

Oracle事务

2010-06-23 20:31:54

2022-08-26 13:48:40

EPUBLinux

2009-08-25 16:27:10

Mscomm控件

2010-07-13 09:36:25

2020-09-21 09:53:04

FlexCSS开发

2020-07-20 06:35:55

BashLinux

2011-07-18 15:08:34

2010-11-15 11:40:44

Oracle表空间

2022-06-03 10:09:32

威胁检测软件

2023-11-14 09:18:00

Python代码

2010-09-27 09:31:42

JVM内存结构

2013-04-16 10:20:21

云存储服务云存储SLA服务水平协议

2018-06-22 13:05:02

前端JavaScript引擎

2021-04-28 10:13:58

zookeeperZNode核心原理

2021-01-19 12:00:39

前端监控代码

2010-11-08 13:54:49

Sqlserver运行

2023-11-02 07:55:31

Python对象编程

2009-09-24 16:22:50

Hibernate自动
点赞
收藏

51CTO技术栈公众号