四时宝库

程序员的知识宝库

Python 中的接口(python软件接口)

Python 是动态类型语言, 只在运行时做 Duck Typing 检查.

  • 利: 灵活, 方便
  • 弊: 代码混乱, 缺少规范

标准自带两类接口支持: abc 和 typing.Protocol, 有他们协助给天马行空的程序员套上枷锁, Python 的大工程才可以"上道"


abc

abc 就是 Abstract Base Class, 虚基类. 跟 Java, C++ 中的虚基类是一个意思, 可以对派生类提供实例化时的动态检查, 确保虚拟接口 (abstractmethod) 都有实现

import abc


class Base(abc.ABC):
    @abstractmethod
    def foo(self, s: str):
        """abc interface demo
        """


class Invalid(Base):
    pass
        
        
class Child(Base):
    def foo(self):
        pass
    
c = Child()
assert isinstance(c, Base)

# TypeError: Can't instantiate abstract class Invalid with abstract methods foo
i = Invalid()

也提供了非侵入式的虚基类关联方法

from abc import ABC

class MyABC(ABC):
    pass

MyABC.register(tuple)

assert issubclass(tuple, MyABC)
assert isinstance((), MyABC)
  • 检查时机: 在运行中当派生类实例化时
  • 检查范围: 只确保 abstractmethod 是否在派生类中有相同函数名实现, 并不检查实现的参数和返回值是否相同. 只看名字不比签名
  • 代码影响: 侵入式, 需要继承. 也有手工非侵入式方案


typing.Protocol

structure subtyping (static duck-typing)

import typing


class Countable(typing.Protocol):
    def count(self, who: str) -> int:
        """support count
        """
        

class Counter:
    def count(self, who: str) -> int:
        return 0
    
c = Counter()

def f(c: Countable):
    c.count("bill")
  • 检查时机: 静态类型检查接口使用方, 例如 mypy
  • 检查范围: 确保实现类按照签名实现了接口的全部函数
  • 代码影响: 非侵入式, 不需要继承


比较

abc 类似 c++ 中的虚基类, typing.Protocol 则好比 c++ 中的 concept.

当然, Python 是动态语言, 在 typing.runtime_checkable 和 abc.abstractmethod 加成后, typing.Protocol 动静两相宜

import typing


@typing.runtime_checkable
class Countable(typing.Protocol):
    @abc.abstractmethod
    def count(self, who: str) -> int:
        """support count
        """
        

class Counter:
    def count(self, who: str) -> int:
        return 0

    
assert issubclass(Counter, Countable)

c = Counter()
assert isinstance(c, Countable)



def f(c: Countable):
    assert isinstance(c, Countable)
    print(c.count("bill"))
    
f(c)

class InvalidCounter(Countable):
    def c(self):
        pass
    
# TypeError: Can't instantiate abstract class InvalidCounter with abstract methods count
i = InvalidCounter()

上面这个终极解决方案兼有两者的优点:

  • 静态类型检查时会确保是否在派生类中有相同签名的实现
  • 动态运行时, 会检查是否同名函数存在
  • 代码影响: 自动非侵入式, 不需要继承, 也无需手工注册

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接