用什么来替换 python 中的 interface/protocol

What to use in replacement of an interface/protocol in python

我正在做一个象棋游戏,想做一个标准棋子interface/protocol。 Python 语言中没有这些,所以我应该使用什么?我读了一些关于工厂的资料,但我不确定它们会有什么帮助。提前致谢!

简而言之,您可能根本不需要担心。由于 Python 使用 duck typing - see also the Wikipedia article 进行更广泛的定义 - 如果一个对象具有正确的方法,它将简单地工作,否则将引发异常。

您可能有一个 Piece 基础 class,其中一些方法抛出 NotImplementedError 以指示它们需要重新实现:

class Piece(object):

    def move(<args>):
        raise NotImplementedError(optional_error_message) 

class Queen(Piece):

    def move(<args>):
        # Specific implementation for the Queen's movements

# Calling Queen().move(<args>) will work as intended but 

class Knight(Piece):
    pass

# Knight().move() will raise a NotImplementedError

或者,您可以通过使用 isinstance or isubclass 显式验证您收到的对象以确保它具有所有正确的方法,或者它是 Piece 的子 class。 请注意,某些人可能不认为检查类型 "Pythonic" 并且使用 NotImplementedError 方法或 abc 模块 - 如 this very good answer 中所述 - 可能更可取。

您的工厂只需生产具有正确方法的对象实例。

我用Python(用tkinter)写了一个国际象棋游戏,我的做法是有一个棋子class,Queen/Knight/etc。 class继承自 Piece class、一个 Player class、一个 Square class 和一个用于 tkinter 主循环的主程序 class。每个 Piece 都有一个颜色和位置,以及一种方法来帮助为直线移动直到被阻挡的棋子生成移动集。具体 Piece subclasses 每个包含一个方法来确定他们的移动集。一个 Square 对象包含一个 Piece 和方块在棋盘上的位置。

主程序 class 有一个 __init__ 用于设置棋盘、放置棋子、加载棋子图标和声音效果以及初始化选项。 draw_board 方法重绘棋盘,重置所有棋子并重新绑定热键。然后还有各种其他方法来加载新图标、开始新游戏、设置音量、保存、撤消、城堡等等。

我还没有完成第 10 版,但您可以获得第 9 版的源代码和资产here

您还可以查看开源 Shane's Chess Information Database。没用过,不过挺好看的

我通常不使用 Python 中的接口,但如果您必须这样做,可以使用 zope.interface。然后,您可以验证 类 或对象是否实现了某些接口。此外,如果 类 没有实现所有方法或属性,它也会引发错误。 Twisted 和其他框架使用这个库。

尽管 Python 是动态的,可以使用鸭子类型,但仍然可以实现 Java 和 C# 调用 "interfaces" 的内容。这是通过声明一个 Abstract Base Class 来完成的。 https://docs.python.org/2/library/abc.html or https://docs.python.org/3.4/library/abc.html

定义 ABC 时,将所有类似接口的方法放入其中,并在其主体中包含 passraise NotImplementedError。子 classes 从您的 ABC 继承并覆盖这些方法,就像任何其他子 class 覆盖父 class 方法一样。 (由于 Python 有多重继承,它们可以继承你的 ABC 加上你喜欢的任何其他 class。)

Python 3.8 中的新功能:

接口和协议的一些好处是在开发过程中使用内置于 IDE 的工具进行类型提示,以及在运行前检测错误的静态类型分析。这样,静态分析工具可以在您检查代码时告诉您是否试图访问任何未在对象上定义的成员,而不是仅在运行时才发现。

typing.Protocol class 被添加到 Python 3.8 作为 "structural subtyping." 的机制其背后的力量在于它可以用作 隐式基础class。也就是说,出于静态类型分析的目的,任何 class 的成员与 Protocol 的已定义成员相匹配都被视为它的子class。

PEP 544 中给出的基本示例展示了如何使用它。

from typing import Protocol

class SupportsClose(Protocol):
    def close(self) -> None:
        # ...

class Resource:
    # ...
    def close(self) -> None:
        self.file.close()
        self.lock.release()

def close_all(things: Iterable[SupportsClose]) -> None:
    for thing in things:
        thing.close()

file = open('foo.txt')
resource = Resource()
close_all([file, resource])  # OK!
close_all([1])     # Error: 'int' has no 'close' method

注意:typing-extensions package 向后移植 typing.Protocol Python 3.5+。

Python 的美妙之处在于不需要接口。由于鸭子打字,你可以只创建几个 类 都具有相同的方法签名:

class Queen:
  def move(self, x, y):
    #do stuff

class Pawn:
  def move(self, x, y):
    # do stuff

这些 类 的实例可以互换使用:

def make_move(piece, x, y):
  piece.move(x, y)

q = Queen()
make_move(q, 0, 0)
p = Pawn()
make_move(p, 4, 5)

请注意,以上绝不是一个成熟的国际象棋游戏的好设计。仅供参考。

Python 中有一种模仿界面的好方法。 在 Python 中生成“接口”时使用 metaclass=ABCMeta class 以及必须为此接口实现的所有方法的 @abstractmethod 装饰器。两者都来自 abc class。 (如果不实现任何此类 @abstractmethod 修饰的方法,而继承,从而实现“接口”class,一旦从此类生成实例,就会引发 NotImplementedError具体 class.)

作为命名约定,所有此类 class 都以大写字母 I 开头(对于 I 接口)。

from abc import ABCMeta, abstractmethod


class IPiece(metaclass=ABCMeta):
    "The Piece Interface"
    
    @abstractmethod
    def move(<args>):
        "NotImplementedError is superfluous, instead, one can use this space"
        "To write some remarks, comments, annotations..."

class Queen(Piece):

    def move(<args>):
        # Specific implementation for the Queen's movements