如何为Python函数参数添加可Pickle类型提示并保留原有类型?
要让编辑器能检测出传入pickle_func的对象是否可Pickle,同时保留原有的Callable[[], None]类型约束,可以通过**自定义类型协议(Protocol)**结合类型交集来实现。Python的静态类型检查工具(如Mypy、Pyright)支持这种方式来约定额外的类型要求。
步骤1:定义「可Pickle」的类型协议
首先,用typing.Protocol定义一个标记性协议,用来表示对象支持Pickle操作。这是一种静态约定,配合类型检查工具就能实现预期的提示效果:
from typing import Protocol, Callable, Intersection import pickle class Pickleable(Protocol): """标记可被pickle序列化的对象""" pass
步骤2:组合类型提示
将原有的Callable[[], None]与Pickleable进行交集组合,作为pickle_func的参数类型。Python 3.10+支持直接用&表示类型交集,更早版本可以用Intersection:
# Python 3.10+ 写法 def pickle_func(func: Callable[[], None] & Pickleable): with open("data.pickle", "wb") as f: pickle.dump(func, f, pickle.HIGHEST_PROTOCOL) # 兼容Python 3.9及以下的写法 # def pickle_func(func: Intersection[Callable[[], None], Pickleable]): # ...
步骤3:标记不可Pickle的函数
为了让类型检查工具识别get_non_pickable()返回的局部函数不可Pickle,给它的返回值标注为不兼容Pickleable协议的类型:
from typing import Callable, TypeVar # 定义不绑定Pickleable的TypeVar NonPickleableCallable = TypeVar("NonPickleableCallable", bound=Callable[[], None]) def get_non_pickable() -> NonPickleableCallable: def not_pickable(): print("I am NOT pickable!") return not_pickable
效果验证
现在传入get_non_pickable()到pickle_func时,Pyright/Mypy这类工具会提示类型不匹配:
Argument of type "NonPickleableCallable" cannot be assigned to parameter "func" of type "Callable[[], None] & Pickleable"
Type "NonPickleableCallable" is incompatible with protocol "Pickleable"
而传入pickable()时则无任何提示,符合预期。
补充:运行时检查(可选)
静态类型提示无法覆盖所有不可Pickle的场景(比如包含不可序列化属性的对象),可以在函数内部添加运行时检查作为补充:
def pickle_func(func: Callable[[], None] & Pickleable): # 运行时验证是否可Pickle try: pickle.dumps(func) except pickle.PicklingError as e: raise ValueError(f"传入的对象不可Pickle: {e}") from e with open("data.pickle", "wb") as f: pickle.dump(func, f, pickle.HIGHEST_PROTOCOL)
内容的提问来源于stack exchange,提问作者Izaak Cornelis

