zope.interface库是一种克服Python接口设计中的歧义的方法。 让我们来看看它。
隐式接口不是zen
Python的Zen足够宽松,并且自相矛盾,因此您可以从中证明一切。 让我们沉思其最著名的原则之一:“显式胜于隐式”。
传统上在Python中隐含的一件事是预期的接口。 已经记录了函数以期望“类文件对象”或“序列”。 但是,什么是文件状对象? 它支持.writelines吗? 那.seek呢? 什么是“序列”? 它是否支持分步切片,例如a [1:10:2] ?
最初,Python的答案是所谓的“鸭型”,它取自短语“如果它像鸭子一样行走,而像鸭子一样嘎嘎叫,那可能就是鸭子。” 换句话说,“尝试一下然后看”,这可能是您可能获得的最隐式的。
Zope Web框架,它迫切需要这些东西以使其变得明显,例如,“类用户对象”所期望的呈现代码。输入zope.interface ,它由Zope开发,但作为单独的Python包发布。 Zope.interface帮助声明存在哪些接口,哪些对象提供它们以及如何查询该信息。
想象一下编写一个简单的2D游戏,它需要各种东西来支持“ sprite”界面。 例如,指示边界框,但也指示对象何时与框相交。 与其他一些语言不同,在Python中,将属性访问作为公共接口的一部分是一种常见做法,而不是实现getter和setter。 边界框应该是属性,而不是方法。
呈现精灵列表的方法可能类似于:
def render_sprites
( render_surface
, sprites
) :
"""
sprites should be a list of objects complying with the Sprite interface:
* An attribute "bounding_box", containing the bounding box.
* A method called "intersects", that accepts a box and returns
True or False
"""
pass
# some code that would actually render
游戏将具有许多处理精灵的功能。 在每个文件中,您都必须在文档字符串中指定期望的合同。
此外,某些功能可能期望使用更复杂的Sprite对象,可能具有Z顺序。 我们必须跟踪哪些方法需要Sprite对象,哪些方法需要SpriteWithZ对象。
能够使sprite是显式且显而易见的,这样方法可以声明“我需要一个sprite”并严格定义该接口,这不是很好吗? 输入zope.interface 。
from zope
import interface
class ISprite
( interface.
Interface
) :
bounding_box
= interface.
Attribute
(
"The bounding box"
)
def intersects
( box
) :
"Does this intersect with a box"
乍一看,这段代码看起来有些奇怪。 这些方法不包括self ,这是一种常见的做法,它具有Attribute属性 。 这是在zope.interface中声明接口的方法 。 这看起来很奇怪,因为大多数人不习惯严格声明接口。
这样做的原因是该界面显示了方法的调用方式,而不是方法的定义方式。 因为接口不是超类,所以可以将它们用于声明数据属性。
接口的一种可能的实现方式可以是圆形精灵:
@ implementer
( ISprite
)
@ attr.
s
( auto_attribs
=
True
)
class CircleSprite:
x:
float
y:
float
radius:
float
@
property
def bounding_box
(
self
) :
return
(
self .
x -
self .
radius
,
self .
y -
self .
radius
,
self .
x +
self .
radius
,
self .
y +
self .
radius
,
)
def intersects
(
self
, box
) :
# A box intersects a circle if and only if
# at least one corner is inside the circle.
top_left
, bottom_right
= box
[ :
2
]
, box
[
2 :
]
for choose_x_from
( top_left
, bottom_right
) :
for choose_y_from
( top_left
, bottom_right
) :
x
= choose_x_from
[
0
]
y
= choose_y_from
[
1
]
if
(
(
( x -
self .
x
) **
2 +
( y -
self .
y
) **
2
)
<=
self .
radius **
2
) :
return
True
return
False
这明确声明CircleSprite类实现了该接口。 它甚至使我们能够验证该类是否正确实现了它:
from zope.
interface
import verify
def test_implementation
(
) :
sprite
= CircleSprite
( x
=
0
, y
=
0
, radius
=
1
)
verify.
verifyObject
( ISprite
, sprite
)
pytest , 鼻子或其他测试运行程序可以运行此程序 ,它将验证所创建的sprite是否符合该接口。 测试通常是局部的:它不会测试文档中仅提及的任何内容,甚至不会测试可以无例外地调用方法! 但是,它会检查是否存在正确的方法和属性。 这是对单元测试套件的一个很好的补充,并且至少可以防止简单的拼写错误通过测试。
翻译自: https://opensource.com/article/19/9/zopeinterface-python-package