如何解决我可以在 Python 中限制子类的类型吗?
假设我想在添加一些有用方法的集合和列表上定义包装类,如下所示:
from abc import ABC
class AbstractGizmo(ABC):
def bloviate(self):
print(f"Let me tell you more about my {len(self)} elements")
class ListGizmo(list,AbstractGizmo):
pass
class SetGizmo(set,AbstractGizmo):
pass
现在我可以打电话了:
>>> ListGizmo([1,2,3]).bloviate()
>>> SetGizmo({1,3}).bloviate()
from typing import Union,Set,List
def bloviate(collection: Union[Set,List]):
print(f"Let me tell you more about my {len(collection)} elements")
class AbstractGizmo(ABC):
def bloviate(self):
return bloviate(self)
所以我也可以这样做:
>>> bloviate([1,3])
>>> bloviate({1,3})
由于子类 ListGizmo
是一个列表,而子类 SetGizmo
是 一个集合,所以这个设置实际上在实践中工作得很好。但是静态类型检查器(如 pyright)不知道这一点,因此它们(正确地)在此处显示错误:
class AbstractGizmo(ABC):
def bloviate(self):
return bloviate(self) # Error: Type 'AbstractGizmo' cannot be assigned
# to type 'Set[UnkNown] | List[UnkNown]'
我是否可以通过某种方式向 Python / pyright 表明,“AbstractGizmo
的所有实例都保证在 Union[Set,List]
中”?这种语法让我无法理解。
(注意,当然在这个简单的例子中,我可以在每个子类上定义 bloviate()
来避免这个问题。实际上我有更多的方法和更多的包装子类,所以如果可以的话我会得到组合爆炸t 将它们抽象为 AbstractGizmo
。)
解决方法
要正确 type mixin classes,请将 self
参数注释为与所需基本类型的功能匹配的 Protocol
:
from typing import Protocol
from abc import ABC
class HasLength(Protocol): # or just `typing.Sized` in this case
def __len__(self) -> int: ...
def bloviate(collection: HasLength):
print(f"Let me tell you more about my {len(collection)} elements")
class AbstractGizmo(ABC):
def bloviate(self: HasLength):
return bloviate(self)
class ListGizmo(list,AbstractGizmo):
pass
ListGizmo().bloviate() # this is fine
请注意,mixin 仍然可以与其他类型组合而不会引发静态类型错误。但是,使用相应的方法会触发运行时和静态类型检查的错误。
class IntGizmo(int,AbstractGizmo):
pass
IntGizmo().bloviate() # error: Invalid self argument "IntGizmo" to attribute function "bloviate" with type "Callable[[HasLength],Any]"
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。