Python 类型 - 限制列表仅允许一种类型的子类

Dem*_*kyy 6 python python-typing

我有 3 个简单的类,例如:

class Animal(abc.ABC):
    ...

class Cat(Animal):
    ...

class Dog(Animal):
    ...
Run Code Online (Sandbox Code Playgroud)

然后我有一个注释如下的函数:

def speak(animals: List[Animal]) -> List[str]:
   ...
Run Code Online (Sandbox Code Playgroud)

我的问题是我想限制List[Animal]只包含一种动物,所以:

speak([Dog(), Dog()]) # OK
speak([Cat(), Cat()]) # OK
speak([Cat(), Dog()]) # typing error
Run Code Online (Sandbox Code Playgroud)

我将如何注释该speak函数以实现此目的?是否可以使用打字来完成,或者我是否被迫在运行时检查这一点?

我尝试使用上面的方法,但是在调用likeList[Animal]时不会出现错误。speakspeak([Cat(), Dog()])

我也尝试过使用泛型,TypeVar('T', bound=Animal)但这仍然允许我传递子List类的任意组合。

dro*_*oze 1

我认为您的问题尚未明确定义。一旦您开始填写更具体的实现Animal,您可能会得到一个令人信服的解决方案。

在这里,我将重新表述您speak目前的标准:您希望它接受list的任何单个子类的a Animal,但不接受Animal其本身。希望我们能明白为什么这没有意义 - 您给定的代码中没有任何内容表明Animal 可以与 的任何子类区分开来,至少从对动物的作用的Animal角度来看是这样。speaklist

让我们提供一些区别特征:

Python 3.10

import typing as t
from typing_extensions import LiteralString
from collections.abc import Sequence

Sound = t.TypeVar("Sound", bound=LiteralString)

class Animal(t.Generic[Sound]):
    def speak(self) -> Sound:
        ...

class Cat(Animal[t.Literal["meow"]]):
    ...

class Dog(Animal[t.Literal["bark"]]):
    ...

def speak(animals: Sequence[Animal[Sound]]) -> list[str]:
    return [animal.speak() for animal in animals]


>>> speak([Dog(), Dog()])  # OK
>>> speak([Cat(), Cat()])  # OK
>>>
>>> # mypy: Argument 1 to "speak" has incompatible type "List[object]"; expected "Sequence[Animal[<nothing>]]" [arg-type]
>>> # pyright: Argument of type "list[Cat | Dog]" cannot be assigned to parameter "animals" of type "Sequence[Animal[Sound@speak]]" in function "speak"
>>> # pyre: Incompatible parameter type [6]: In call `speak`, for 1st positional only parameter expected `Sequence[Animal[Variable[Sound (bound to typing_extensions.LiteralString)]]]` but got `List[Union[Cat, Dog]]`
>>> speak([Cat(), Dog()])
Run Code Online (Sandbox Code Playgroud)

请注意,虽然mypy不会抱怨签名squeak(animals: list[Animal[Sound]]),但这在技术上不是类型安全的;您可以决定附加Cat()list[Dog]. 这就是使用 的原因Sequence(它的元素类型是不可变且协变的)。