【问题标题】:How to inherit a class which is inheriting `list` with correct type hint in Python?如何在 Python 中继承具有正确类型提示的 `list` 的类?
【发布时间】:2023-02-24 02:46:33
【问题描述】:

例如,我有两个类:AnimalCat

class Animal:
    pass


class Cat(Animal):
    pass

然后,我想做一个Animal的列表,这样我就可以写一些函数来管理这个Animal的列表。在这里创建一个继承list的类是很直观的。

from typing import List


class AnimalList(List[Animal]):
    def get_element(self) -> Animal:  # get one element, just for example
        return self[0]

(看过this等问题,我觉得继承list很可能是我想要的,而不是创建一个以list为属性的类。)

之后,我发现我还需要一个类,这样我就可以管理一个Cat的列表。 Cat 有一些它的基础 Animal 没有的功能。所以,我需要AnimalList 中的函数和只能通过Cat 列表完成的特殊函数。也就是说,我需要一个满足以下要求的类CatList

  1. 我可以管理 Cat 的列表,就像我管理 list 一样。例如,我可以这样做:
    # I want to do something like:
    cat = Cat()
    cat_list = CatList()
    cat_list.append(cat)  # manipulate this CatList like list
    cat_1 = cat_list[0]
    
    
    1. CatList可以继承AnimalList中的方法,也有自己的方法。例如,我可以这样做:
    # I want to do something like:
    cat_list.play_with_cats()  # Own function
    
    cat_2 = cat_list.get_element()  # Base function
    
    1. CatList.__getitem__()(上面的cat_1)的类型提示是Cat
    2. CatList.get_element()(上面的cat_2)等基类方法的结果类型提示是Cat

    问题: 如何创建满足上述要求的类CatListCatList 应该继承的类是什么?

    我已经尝试了几种方法来做到这一点。

    尝试 1

    继承自AnimalList

    class CatList(AnimalList):
        def play_with_cats(self) -> None:
            print("Play with cats!")
            
        def __getitem__(self, item) -> Cat:
            return self.__getitem__(item)
        
        def get_element(self) -> Cat:
            return super().get_element()
    

    但我需要重写__getitem__()get_element()等所有函数,以确保这些函数对应的类型提示是Cat。这太麻烦了。

    尝试 2

    继承List[Cat]AnimalList

    class CatList(List[Cat], AnimalList):
        def play_with_cats(self) -> None:
            print("Play with cats!")
    

    但是 CatList.__getitem__()get_element() 结果的类型提示变为 Any,而不是 Cat

【问题讨论】:

  • 尚未在 Python 中使用它,但您可能有兴趣查看 Python's Generics 以帮助解决此问题。

标签: python list inheritance type-hinting


【解决方案1】:

假设您有以下课程:

class Animal:
    pass


class Cat(Animal):
    pass


class Dog(Animal):
    pass

listtuple 等标准集合类型的美妙之处在于,自 Python 3.9 起,它们都是实际的泛型,这意味着它们实现了 __class_getitem__ 方法。它返回generic alias types

因此,我们可以利用 list 就其元素类型而言已经是通用的这一事实。我们只需设置一个上限为 Animal 的类型变量,然后使用该类型变量参数化 list 的子类。然后子类基本上自己写:

from typing import TypeVar


A = TypeVar("A", bound=Animal)


class AnimalList(list[A]):
    def get_element(self) -> A:
        return self[0]


class CatList(AnimalList[Cat]):
    pass


class DogList(AnimalList[Dog]):
    pass

演示:(传递给 mypy)

dl = DogList([Dog()])
cl = CatList([Cat()])
dog = dl.get_element()
cat = cl[0]
reveal_type(dog)  # note: Revealed type is "Dog"
reveal_type(cat)  # note: Revealed type is "Cat"

它带有list 拥有的所有常用打字功能,而不仅仅是__getitem__。如果我们尝试dl.append(Cat())之类的东西,类型检查器就会对我们大喊大叫。

关于Python中泛型的细节,我总是参考大名鼎鼎的PEP 484

【讨论】:

    猜你喜欢
    • 2018-05-26
    • 2020-06-14
    • 2019-03-16
    • 2021-08-13
    • 2019-05-10
    • 2014-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多