Python类型提示:如何让参数仅接收父类实例而非子类?
为类方法参数添加仅接受父类实例的类型提示
先看初始代码:
class Foo: @classmethod def bar(cls, baz): pass class Egg(Foo): pass egg = Egg() egg.bar(Foo())
需求明确:给bar方法的baz参数添加类型提示,使其仅接受Foo类的实例,子类Egg的实例不允许传入。
尝试过的方案及问题
方案1:使用Self类型
修改后的代码:
from typing import Self class Foo: @classmethod def bar(cls, baz: Self): pass class Egg(Foo): pass egg = Egg() egg.bar(Foo())
用mypy静态检查时会报错:
Argument 1 to "bar" of "Foo" has incompatible type "Foo"; expected "Egg" [arg-type]
原因是Self的语义是调用该方法的类的实例类型:当通过Egg实例调用bar时,Self会被解析为Egg,要求传入Egg实例,和我们需要的“仅接受Foo实例”完全相悖。
方案2:直接写baz: Foo
尝试将类型提示写成def bar(cls, baz: Foo):,但代码检查工具会提示undefined name 'Foo'——这是因为定义bar方法时,Foo类还未完成定义,属于前向引用问题。
解决方法
最优方案:使用from __future__ import annotations
Python 3.7及以上版本支持通过from __future__ import annotations让所有类型注解延迟求值,既解决前向引用问题,又无需手动给类名加引号:
from __future__ import annotations class Foo: @classmethod def bar(cls, baz: Foo): pass class Egg(Foo): pass egg = Egg() egg.bar(Foo()) # mypy检查通过,符合需求 egg.bar(Egg()) # mypy会报错,拦截子类实例传入
这种写法比手动写"Foo"更简洁清晰,是官方推荐的前向引用解决方案。
备选方案:使用typing.ForwardRef(不推荐)
如果不想启用未来特性,也可以用ForwardRef显式声明前向引用,但写法繁琐:
from typing import ForwardRef FooType = ForwardRef('Foo') class Foo: @classmethod def bar(cls, baz: FooType): pass class Egg(Foo): pass # Python 3.9及以下版本需手动解析前向引用 from typing import get_type_hints get_type_hints(Foo.bar)
该方案仅适合特殊场景使用,远不如__future__ annotations便捷。
内容的提问来源于stack exchange,提问作者whoami
相关产品推荐
相关产品推荐

