翻译完成

This commit is contained in:
MjSeven 2020-01-01 23:00:27 +08:00
parent c974b29f96
commit f782f892a9
2 changed files with 125 additions and 132 deletions

View File

@ -1,132 +0,0 @@
[#]: collector: (lujun9972)
[#]: translator: (MjSeven)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (An advanced look at Python interfaces using zope.interface)
[#]: via: (https://opensource.com/article/19/9/zopeinterface-python-package)
[#]: author: (Moshe Zadka https://opensource.com/users/moshezhttps://opensource.com/users/lauren-pritchetthttps://opensource.com/users/sethhttps://opensource.com/users/drmjg)
An advanced look at Python interfaces using zope.interface
======
Zope.interface helps declare what interfaces exist, which objects
provide them, and how to query for that information.
![Snake charmer cartoon with a yellow snake and a blue snake][1]
The **zope.interface** library is a way to overcome ambiguity in Python interface design. Let's take a look at it.
### Implicit interfaces are not zen
The [Zen of Python][2] is loose enough and contradicts itself enough that you can prove anything from it. Let's meditate upon one of its most famous principles: "Explicit is better than implicit."
One thing that traditionally has been implicit in Python is the expected interface. Functions have been documented to expect a "file-like object" or a "sequence." But what is a file-like object? Does it support **.writelines**? What about **.seek**? What is a "sequence"? Does it support step-slicing, such as **a[1:10:2]**?
Originally, Python's answer was the so-called "duck-typing," taken from the phrase "if it walks like a duck and quacks like a duck, it's probably a duck." In other words, "try it and see," which is possibly the most implicit you could possibly get.
In order to make those things explicit, you need a way to express expected interfaces. One of the first big systems written in Python was the [Zope][3] web framework, and it needed those things desperately to make it obvious what rendering code, for example, expected from a "user-like object."
Enter **zope.interface**, which is developed by Zope but published as a separate Python package. **Zope.interface** helps declare what interfaces exist, which objects provide them, and how to query for that information.
Imagine writing a simple 2D game that needs various things to support a "sprite" interface; e.g., indicate a bounding box, but also indicate when the object intersects with a box. Unlike some other languages, in Python, attribute access as part of the public interface is a common practice, instead of implementing getters and setters. The bounding box should be an attribute, not a method.
A method that renders the list of sprites might look like:
```
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
```
The game will have many functions that deal with sprites. In each of them, you would have to specify the expected contract in a docstring.
Additionally, some functions might expect a more sophisticated sprite object, maybe one that has a Z-order. We would have to keep track of which methods expect a Sprite object, and which expect a SpriteWithZ object.
Wouldn't it be nice to be able to make what a sprite is explicit and obvious so that methods could declare "I need a sprite" and have that interface strictly defined? Enter **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"
```
This code looks a bit strange at first glance. The methods do not include a **self**, which is a common practice, and it has an **Attribute** thing. This is the way to declare interfaces in **zope.interface**. It looks strange because most people are not used to strictly declaring interfaces.
The reason for this practice is that the interface shows how the method will be called, not how it is defined. Because interfaces are not superclasses, they can be used to declare data attributes.
One possible implementation of the interface can be with a circular sprite:
```
@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
```
This _explicitly_ declares that the **CircleSprite** class implements the interface. It even enables us to verify that the class implements it properly:
```
from zope.interface import verify
def test_implementation():
    sprite = CircleSprite(x=0, y=0, radius=1)
    verify.verifyObject(ISprite, sprite)
```
This is something that can be run by **pytest**, **nose**, or another test runner, and it will verify that the sprite created complies with the interface. The test is often partial: it will not test anything only mentioned in the documentation, and it will not even test that the methods can be called without exceptions! However, it does check that the right methods and attributes exist. This is a nice addition to the unit test suite and—at a minimum—prevents simple misspellings from passing the tests.
--------------------------------------------------------------------------------
via: https://opensource.com/article/19/9/zopeinterface-python-package
作者:[Moshe Zadka][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://opensource.com/users/moshezhttps://opensource.com/users/lauren-pritchetthttps://opensource.com/users/sethhttps://opensource.com/users/drmjg
[b]: https://github.com/lujun9972
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/getting_started_with_python.png?itok=MFEKm3gl (Snake charmer cartoon with a yellow snake and a blue snake)
[2]: https://en.wikipedia.org/wiki/Zen_of_Python
[3]: http://zope.org

View File

@ -0,0 +1,125 @@
[#]: collector: (lujun9972)
[#]: translator: (MjSeven)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (An advanced look at Python interfaces using zope.interface)
[#]: via: (https://opensource.com/article/19/9/zopeinterface-python-package)
[#]: author: (Moshe Zadka https://opensource.com/users/moshezhttps://opensource.com/users/lauren-pritchetthttps://opensource.com/users/sethhttps://opensource.com/users/drmjg)
借助 zope.interface 深入了解 Python 接口
======
Zope.interface 可以帮助指出存在哪些接口,是由哪些对象提供的,以及如何查询这些信息。
![Snake charmer cartoon with a yellow snake and a blue snake][1]
**zope.interface** 库可以克服 Python 接口设计中的歧义性。让我们来研究一下。
### 隐式接口不是 Python 之禅
[Python 之禅][2] 很宽松,但是有点自相矛盾,你可以从中证明一切。让我们来思考其中最著名的原则之一:“显示胜于隐式”。
在 Python 中会隐含的一件事是预期的接口。比如函数已经记录了它期望一个“类文件对象”或“序列”。但是什么是类文件对象呢?它支持 **.writelines** 和 **.seek** 吗?什么是一个“序列”?是否支持分步切片,例如 **a[1:10:2]**
最初Python 的答案是所谓的“鸭子类型”,取自短语“如果它像鸭子一样行走,像鸭子一样嘎嘎叫,那么它可能就是鸭子”。换句话说,“试试看”,这可能是你能得到的最含蓄的表达。
为了使这些内容显式地表达出来,你需要一种方法来表达期望的接口。最早用 Python 编写的大型系统之一是 [Zope][3] Web 框架,它迫切需要这些东西来明确地呈现代码,例如,期望从“类似用户的对象”获得什么。
**zope.interface** 由 Zope 开发,但作为单独的 Python 包发布。**Zope.interface** 可以帮助指出存在哪些接口,是由哪些对象提供的,以及如何查询这些信息。
想象编写一个简单的 2D 游戏,它需要各种东西来支持 sprite 接口to 校正:这里不知道如何翻译)。例如,表示一个边界框,但也表示对象何时与一个框相交。与一些其他语言不同,在 Python 中,将属性访问作为公共接口一部分是一种常见的做法,而不是实现 getter 和 setter。边界框应该是一个属性而不是一个方法。
呈现外观列表的方法可能类似于:
```
def render_sprites(render_surface, sprites):
    """
    sprites 应该是符合 Sprite 接口的对象列表:
    * 一个属性 "bounding_box",包含了边界框
    * "intersects" 方法,它接受一个 box 并返回 True 或 False
    """
    pass # 一些代码会渲染
```
游戏将具有许多处理 sprite 的功能(函数)。在每个功能(函数)中,你都必须在文档中指定预期。
此外,某些功能可能期望使用更复杂的 Sprite 对象,例如具有 Z-order 的对象。我们必须跟踪哪些方法需要 Sprite 对象,哪些方法需要 SpriteWithZ 对象。
如果能够使 sprite 的内容显式出来,这样方法就可以声明“我需要一个 sprite”并严格定义接口这不是很好吗来看看 **zope.interface**
```
from zope import interface
class ISprite(interface.Interface):
    bounding_box = interface.Attribute(
        "The bounding box"
    )
    def intersects(box):
        "它和一个框相交吗?"
```
乍看起来,这段代码有点奇怪。这些方法不包括 "self",而包含 "self" 是一种常见的做法,并且它有一个属性。这是在 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):
        # 当且仅当至少一个角在圆内时,方框与圆相交
        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**、**nose** 或其他测试框架运行,它将验证创建的 sprite 是否符合接口。测试通常是局部的:它不会测试文档中没有提及的内容,甚至不会测试方法是否可以在没有异常的情况下被调用!但是,它会检查是否存在正确的方法和属性。这是对单元测试套件一个很好的补充,至少可以防止简单的拼写错误通过测试。
--------------------------------------------------------------------------------
via: https://opensource.com/article/19/9/zopeinterface-python-package
作者:[Moshe Zadka][a]
选题:[lujun9972][b]
译者:[MjSeven](https://github.com/MjSeven)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://opensource.com/users/moshezhttps://opensource.com/users/lauren-pritchetthttps://opensource.com/users/sethhttps://opensource.com/users/drmjg
[b]: https://github.com/lujun9972
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/getting_started_with_python.png?itok=MFEKm3gl (Snake charmer cartoon with a yellow snake and a blue snake)
[2]: https://en.wikipedia.org/wiki/Zen_of_Python
[3]: http://zope.org