在自定义生成器上的filter()函数中键入注释



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

也许我弄错了,但是正如我所理解的,filter()函数中的Color类型注释是说该函数将导致ColorIterable,这正是我想要的。但是当我试图注释这个函数时,我得到了错误。(但是,为什么类型注释会阻止程序运行呢?我认为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)

问题是泛型不是语言级别的添加,而是库中的添加。指定泛型类型参数实际上使用与用于集合中的项访问相同的[]操作符,只是它是在元类上定义的。由于这个原因,泛型语法最初只适用于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",filtertype的实例,[]是订阅操作符)。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]感兴趣。

TL;博士:就类型注释而言,filter不是泛型类,因此它不支持[]操作

最新更新