【问题标题】:Type annotation in a filter() function over a custom generator在自定义生成器上的 filter() 函数中键入注释
【发布时间】:2022-11-16 03:15:30
【问题描述】:

你能帮我理解为什么我在下面的代码中收到 TypeError: 'type' object is not subscriptable 错误吗?

也许我弄错了,但据我所知,filter() 函数中的 Color 类型注释表示该函数将导致 IterableColor ,这正是我想要的。但是当我尝试注释该函数时,我得到了错误。 (但是 waty,类型注释怎么会阻止程序运行?我认为 Python 中的类型提示只会在你的 IDE 中起作用,而不是在运行时)。

对此有任何了解将不胜感激。

# -*- coding: utf-8 -*-
from __future__ import annotations
from typing import TypeVar, Any, Generic, Iterator, Iterable
from abc import ABC, abstractmethod
from dataclasses import dataclass

T = TypeVar('T', bound=Any)
I = TypeVar('I', bound=Any)

class AbstractGenerator(ABC, Iterator[T], Generic[T, I]):
    def __init__(self):
        super().__init__()

        self._items = None
        self._next_item = None

    @property
    def items(self) -> Any:
        return self._items

    @items.setter
    def items(self, items: Any) -> AbstractGenerator:
        self._items = items

        return self

    @property
    def next_item(self) -> Any:
        return self._next_item

    @next_item.setter
    def next_item(self, next_item: Any) -> AbstractGenerator:
        self._next_item = next_item

        return self

    @abstractmethod
    def __len__(self) -> int:
        pass

    @abstractmethod
    def __iter__(self) -> Iterable[T]:
        pass

    @abstractmethod
    def __next__(self) -> Iterable[T]:
        pass

    @abstractmethod
    def __getitem__(self, id: I) -> Iterable[T]:
        pass

ColorId = int

@dataclass(frozen=True)
class Color:
    id: ColorId
    name: str

class MyColorsGenerator(AbstractGenerator[Color, int]):
    def __init__(self):
        super().__init__()
        
        self._colors: list[Color] = []
        self._next_color_index: int = 0 #None
        
    @property
    def colors(self) -> list[Color]:
        return self._colors
        
    @colors.setter
    def colors(self, colors: list[Color]) -> MyColorsGenerator:
        self._colors = colors
        
        return self
    
    @property
    def next_color_index(self) -> int:
        return self._next_color_index

    @next_color_index.setter
    def next_color_index(self, next_color_index: int) -> MyColorsGenerator:
        self._next_color_index = next_color_index
        
        return self
        
    def add_color(self, color: Color) -> MyColorsGenerator:
        self.colors.append(color)
        
        return self
        
    def __len__(self) -> int:
        return len(self.colors)

    def __iter__(self) -> Iterable[Color]:
        return self

    def __next__(self) -> Iterable[Color]:
        if self.next_color_index < len(self.colors):
            self.next_color_index += 1

            return self.colors[self.next_color_index - 1]
        
        else:
            raise StopIteration

    def __getitem__(self, id: ColorId) -> Iterable[Color]:
        return list(filter[Color](lambda color: color.id == id, self.colors))   
        
colors_generator: MyColorsGenerator = MyColorsGenerator()

colors_generator \
    .add_color(Color(id=0, name="Blue")) \
    .add_color(Color(id=1, name="Red")) \
    .add_color(Color(id=2, name="Yellow")) \
    .add_color(Color(id=3, name="Green")) \
    .add_color(Color(id=4, name="White")) \
    .add_color(Color(id=5, name="Black"))

# This results in: TypeError: 'type' object is not subscriptable
#colors: Optional[list[Color]] = list(filter[Color](lambda color: color.id == 4, colors_generator))

# This works, notice the only thing I did was to remove the type annotation for the expected generic type ([Color])    
colors: Optional[list[Color]] = list(filter(lambda color: color.id == 4, colors_generator))
print(colors)

【问题讨论】:

    标签: python types iterator generator


    【解决方案1】:

    问题是泛型不是语言级别的添加,而是库的添加。指定泛型类型参数实际上使用与集合中的项目访问相同的 [] 运算符,只是它是在元类上定义的。由于这个原因,泛型语法最初只适用于typing 模块(typing.List[int]typing.Dict[str, str] 等)中的特定类。然而,从 python3.9 开始,标准库中的一些通用类已被扩展以支持相同的操作,为简洁起见,如list[int]dict[str, str]。这仍然不是语言特性,标准库中的大多数类都没有实现它。此外,正如您正确地注意到的那样,这些注释对解释器(几乎)没有任何意义,并且(大部分)只是为了 ide。除其他外,这意味着您不会将泛型类实例化为专门的泛型(list() 是正确的,list[int]() 是合法的,但毫无意义,被认为是一种不好的做法)。 filter是标准库中的一个类,它不提供通用别名[]操作,所以你会得到应用它没有实现的错误(“'type' object is not subscriptable”,filter是@的一个实例987654333@,[] 是订阅运营商)。 Python 作为语言不理解泛型的概念,因此它无法为您提供更好的错误消息,如“'filter' is not a generic class”。然而,即使它是,您也不应该以这种方式调用它。

    应特别注意泛型函数。它们不能显式提供通用参数。所以,如果我们谈论的不是filter,而是像这样的函数:

    T = typing.TypeVar("T")
    
    def my_filter(f: typing.Callable[[T], bool], seq: list[T]) -> list[T]:
        ...
    

    ,将无法明确告诉您对 my_filter[Color] 感兴趣。

    长话短说:filter在类型注解上不是泛型类,所以不支持[]操作

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-12-12
      • 2012-02-09
      • 2013-06-15
      • 2022-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多