3

假设我想在集合和列表上定义包装类,以添加一些有用的方法,如下所示:

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, 2, 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, 2, 3])
>>> bloviate({1, 2, 3})

由于 subclassListGizmo 一个列表,而 subclassSetGizmo 一个集合,所以这个设置在实践中实际上工作得很好。但是静态类型检查器(如 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()每个子类来避免这个问题。实际上我有更多的方法和更多的包装子类,所以如果我不能将它们抽象为AbstractGizmo.)

4

1 回答 1

0

要正确键入 mixin classesself ,请将参数注释为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]"
于 2021-03-04T18:02:31.747 回答