# Protocol(3.8)

Protocol类是3.8新出的特性,主要用于类型注释时提示对象需要实现哪些些方法,类似于Java的Interface。

# 概述

Protocol​ 对 python 的主要好处在于处理外部库时,我们无法直接修改类型。当需要创建多态函数(可能接受外部库类或我们自己的类之一)时,我们没有共同的祖先来注释它。指定 Protocol​ 可以避免此问题。它允许我们仅定义我们期望带注释的参数具有的行为。

如果我们需要使用Protocol做子类issubclass​或实例isinstance​的判断,则需要在Protocol类前加一个@runtime_checkable​装饰器。

这种方式类似于iterable(isinstance(MyIterable(), Iterable)​)的检查,隶属于python的运行时协议。

# 示例

# 运行

如下代码可以正常运行:

如果解除注释执行(dog).quack会报AttributeError。

import time
from typing import Protocol,runtime_checkable


@runtime_checkable
class DuckLike(Protocol):
    def walk(self, to: str) -> None:
        ...

    def quack(self) -> str:
        ...


class Chicken:
    def walk(self, to):
        time.sleep(5)
        self.location = to

    def quack(self):
        return "tok"


# dont have method quack
class Dog:
    def walk(self, to):
        time.sleep(1)
        self.location = to


def add_to_zoo(duck: DuckLike):
    print(
        f"Adding {duck.__class__.__name__} to the zoo. "
    )
    # duck.walk("zoo")
    # duck.quack()


add_to_zoo(Chicken())  # Type checker allows this
add_to_zoo(Dog())  # Type checker donesnt allow this

print(isinstance(Chicken(), DuckLike))  # True
print(isinstance(Dog(), DuckLike))      # False

print(issubclass(Chicken, DuckLike))    # True
print(issubclass(Dog, DuckLike))        # False

# 代码检查

但是执行代码检查会报错:

(test) lei@leideMacBook-Pro test % python -m mypy test.py 
test.py:38: error: Argument 1 to "add_to_zoo" has incompatible type "Dog"; expected "DuckLike"  [arg-type]
test.py:38: note: "Dog" is missing following "DuckLike" protocol member:
test.py:38: note:     quack
Found 1 error in 1 file (checked 1 source file)

# 子类检查

print(isinstance(Chicken(), DuckLike))  # True
print(isinstance(Dog(), DuckLike))      # False

print(issubclass(Chicken, DuckLike))    # True
print(issubclass(Dog, DuckLike))        # False