微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

我可以在 Python 中限制子类的类型吗?

如何解决我可以在 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()

但我也希望将 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 举报,一经查实,本站将立刻删除。