简体   繁体   English

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

[英]Type annotation in a filter() function over a custom generator

could you help me understand why I am getting the TypeError: 'type' object is not subscriptable error with the code below?你能帮我理解为什么我会收到TypeError: 'type' object is not subscriptable error with below code 吗?

Maybe I'm getting this wrong, but as I understood the Color type annotation in the filter() function is saying that the function will result in an Iterable of Color , which is exactly what I want.也许我弄错了,但据我所知, filter() function 中的Color类型注释是说 function 将导致ColorIterable ,这正是我想要的。 But when I try to annotate the function I get the error.但是当我尝试注释 function 时,我得到了错误。 ( but the waty, how come a type annotation is preventing the program to run? I thought that type hints in in Python would just matter inside your IDE, not in runtime). (但是 waty,类型注释怎么会阻止程序运行?我认为 Python 中的类型提示只会在您的 IDE 中起作用,而不是在运行时)。

Any light on this would be much appreciated.对此有任何了解将不胜感激。

# -*- 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)

The issue is that generics aren't a language-level addition, but a library one.问题是 generics 不是语言级的添加,而是库的添加。 Specifying the generic type parameters actually employs the same [] operator you use for item access in collections, except it is defined on the metaclass.指定泛型类型参数实际上使用与 collections 中用于项目访问的相同[]运算符,只是它是在元类上定义的。 For this reason the generics syntax originally only worked with specific classes in the typing module ( typing.List[int] , typing.Dict[str, str] , etc.).出于这个原因,generics 语法最初只适用于typing模块中的特定类( typing.List[int]typing.Dict[str, str]等)。 Since python3.9, however, some common classes from the standard library have been extended to support the same operation, for brevity, like list[int] , dict[str, str] .然而,自 python3.9 以来,为了简洁起见,标准库中的一些通用类已被扩展以支持相同的操作,例如list[int]dict[str, str] This is still NOT a language feature, and most classes in the standard library do not implement it.这仍然不是语言特性,标准库中的大多数类都没有实现它。 Moreover, as you've rightfully noticed, these annotations carry (almost) no meaning for the interpreter, and are (mostly) just there for the ide. Among other things, that implies that you don't instantiate generic classes as specialized generics ( list() is correct, list[int]() is legal, but pointless and considered a bad practice).此外,正如您正确注意到的那样,这些注释(几乎)对解释器没有任何意义,并且(大部分)仅用于 ide。除其他外,这意味着您不会将泛型类实例化为专门的 generics( list()是正确的, list[int]()是合法的,但毫无意义,被认为是一种不好的做法)。 filter is a class in the standard library, which does not provide the generic-aliasing [] operation, so you get the error that applying it is not implemented (" 'type' object is not subscriptable ", filter is an instance of type , and [] is the subscription operator). filter是标准库中的一个 class,它不提供通用别名[]操作,所以你会得到应用它没有实现的错误(“ 'type' object is not subscriptable ”, filtertype的一个实例, []是订阅运营商)。 Python as the language does not understand the concept of a generic, and so it cannot give you a better error message like " 'filter' is not a generic class ". Python 因为语言不理解泛型的概念,所以它不能给你一个更好的错误信息,比如“ 'filter' is not a generic class ”。 Even if it was, however, you shouldn't have invoked it this way.然而,即使它是,您也不应该以这种方式调用它。

A special note should be made about generic functions.应特别注意泛型函数。 They CANNOT be explicitly supplied with generic parameters.它们不能显式提供通用参数。 So, if instead of filter we were talking about some function like:因此,如果我们谈论的不是filter ,而是一些 function,例如:

T = typing.TypeVar("T")

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

, there would have been no way to explicitly tell you're interested in my_filter[Color] . ,将无法明确告诉您对my_filter[Color]感兴趣。

TL;DR: filter is not a generic class in terms of type annotations, so it does not support the [] operation TL;DR: filter在类型注解方面不是通用的 class,所以不支持[]操作

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM