标签: python-typing

Python 泛型缺少交集类型的解决方法?

我遇到了一个可以通过交叉类型轻松解决的问题(目前正在讨论但尚未实施),并且想知道最干净的解决方法是什么。

当前设置和问题

我当前的设置大致对应于以下动物的 ABC 层次结构。有许多动物“特征”(CanFlyCanSwim等)被定义为抽象子类(尽管它们也可以被定义为 mixin)。

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def name(self) -> str: ...  
    
class CanFly(Animal):
    @abstractmethod
    def fly(self) -> None: ...
    
class CanSwim(Animal):
    @abstractmethod
    def swim(self) -> None: ...
Run Code Online (Sandbox Code Playgroud)

以此我定义了特定的动物类别,包括抽象的和具体的:

class Bird(CanFly):
    def fly(self) -> None:
        print("flap wings")
    
class Penguin(Bird, CanSwim):
    def name(self) -> str:
        return "penguin"
    def swim(self) -> None:
        print("paddle flippers")
Run Code Online (Sandbox Code Playgroud)

我还定义了一个通用类来抚摸特定类型的动物:

from typing import Generic, TypeVar

T = TypeVar("T", bound=Animal, contravariant=True)

class Petter(Generic[T], ABC):

    @abstractmethod …
Run Code Online (Sandbox Code Playgroud)

python abc python-3.x mypy python-typing

24
推荐指数
1
解决办法
4054
查看次数

在运行时验证 Python TypedDict

我正在 Python 3.8+ Django/Rest-Framework 环境中工作,在新代码中强制执行类型,但建立在许多无类型的遗留代码和数据之上。我们广泛使用 TypedDicts 来确保我们生成的数据以正确的数据类型传递到 TypeScript 前端。

MyPy/PyCharm/等。在检查我们的新代码是否输出符合要求的数据方面做得很好,但我们想测试我们的许多 RestSerializers/ModelSerializers 的输出是否符合 TypeDict。如果我有一个序列化器并输入如下字典:

class PersonSerializer(ModelSerializer):
    class Meta:
        model = Person
        fields = ['first', 'last']

class PersonData(TypedDict):
    first: str
    last: str
    email: str
Run Code Online (Sandbox Code Playgroud)

然后运行如下代码:

person_dict: PersonData = PersonSerializer(Person.objects.first()).data
Run Code Online (Sandbox Code Playgroud)

静态类型检查器无法发现person_dict缺少所需email密钥,因为(根据 PEP-589 的设计)它只是一个普通的dict. 但我可以写这样的东西:

annotations = PersonData.__annotations__
for k in annotations:
    assert k in person_dict  # or something more complex.
    assert isinstance(person_dict[k], annotations[k])
Run Code Online (Sandbox Code Playgroud)

它会发现email序列化器的数据丢失了。在这种情况下,这很好,我没有引入任何更改from __future__ import annotations(不确定这是否会破坏它),并且我的所有类型注释都是裸类型。但如果PersonData定义如下:

class PersonData(TypedDict):
    email: Optional[str] …
Run Code Online (Sandbox Code Playgroud)

django-rest-framework mypy python-typing typeddict

21
推荐指数
2
解决办法
9429
查看次数

如何动态定义“typing.Union”?

我在几个项目中使用Typeguard在 Python 运行时进行类型检查。它运作得很好。

我遇到过一种情况,函数参数的类型是typing.Union由一些动态收集的数据类型组成的。例如

def find_datatypes():
    # some stuff ...
    return (str, int) # dynamically generated list / tuple

datatypes = find_datatypes()
Run Code Online (Sandbox Code Playgroud)

现在我想生成一个typing.Unionfromdatatypes以便最终在函数中使用。我期望解包语法能够工作:

my_union = typing.Union[*datatypes]

@typeguard.typechecked
def some_function(param: my_union):
    pass
Run Code Online (Sandbox Code Playgroud)

然而,它并没有:

    my_union = typing.Union[*datatypes]
                            ^
SyntaxError: invalid syntax
Run Code Online (Sandbox Code Playgroud)

我怎样才能实现我想要的目标?

python type-hinting dynamic-typing python-typing

20
推荐指数
2
解决办法
5033
查看次数

Generic 和 TypeVar 的使用

我无法理解Genericand的用法TypeVar以及它们之间的关系。 https://docs.python.org/3/library/typing.html#building-generic-types

文档有这个例子:

class Mapping(Generic[KT, VT]):
    def __getitem__(self, key: KT) -> VT:
        ...
        # Etc.
Run Code Online (Sandbox Code Playgroud)
X = TypeVar('X')
Y = TypeVar('Y')

def lookup_name(mapping: Mapping[X, Y], key: X, default: Y) -> Y:
    try:
        return mapping[key]
    except KeyError:
        return default
Run Code Online (Sandbox Code Playgroud)

类型变量的存在主要是为了静态类型检查器的利益。它们用作泛型类型以及泛型函数定义的参数。

为什么我不能简单地使用Mapping某些现有类型,例如int, 而不是创建Xand Y

python type-hinting python-typing

20
推荐指数
1
解决办法
2万
查看次数

到处使用“from __future__ import 注解”有什么缺点吗?

PEP 563来看,似乎应该有轻微的性能提升,因为它解决了

类型提示在模块导入时执行,这在计算上不是免费的。

那么...我是否可以/应该将其包含from __future__ import annotations在包中的每个文件中,或者是否有任何原因应将其从某些文件中排除?

python annotations python-typing

17
推荐指数
1
解决办法
8594
查看次数

类型提示返回 NameError: name 'datetime' 未定义

我在下面有这个功能;

def time_in_range(start, end, x):
    """Return true if x is in the range [start, end]"""
    if start <= end:
        return start <= x <= end
    else:
        return start <= x or x <= end
Run Code Online (Sandbox Code Playgroud)

函数参数均为日期时间类型。我想向函数添加输入提示。这就是我所做的;

def time_in_range(start: datetime, end: datetime, x: datetime) -> bool:
    """Return true if x is in the range [start, end]"""
    if start <= end:
        return start <= x <= end
    else:
        return start <= x or x <= end
Run Code Online (Sandbox Code Playgroud)

我收到错误NameError: name 'datetime' is not defined …

python type-hinting python-3.x python-typing

16
推荐指数
2
解决办法
1万
查看次数

Python 3中__total__ dunder属性的含义是什么?

在新发布的Python 3.8中,有一个新类型的注释typing.TypedDict。其文档中提到

可以通过Point2D.__annotations__和访问用于自省的类型信息Point2D.__total__。[....]

尽管__annotations__是众所周知的,但已在PEP 3107中进行了介绍,但我在上找不到任何信息__total__。谁能解释它的含义,并在可能的情况下链接到权威资料来源?

python python-3.x python-typing

16
推荐指数
1
解决办法
289
查看次数

变量“foo_class”作为类型无效,但为什么呢?

我有类似的东西:

from typing import Type


class Foo:
    pass


def make_a_foobar_class(foo_class: Type[Foo]) -> Type[Foo]:

    class FooBar(foo_class):
        # this.py:10: error: Variable "foo_class" is not valid as a type
        # this.py:10: error: Invalid base class "foo_class"
        pass

    return FooBar


print(make_a_foobar_class(Foo)())
Run Code Online (Sandbox Code Playgroud)

运行会mypy在该行抛出这两个错误(作为注释添加 ^)class FooBar(foo_class):

该代码似乎工作得很好:

$ python this.py
<__main__.make_a_foobar_class.<locals>.FooBar object at 0x10a422be0>
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?

python-3.x mypy python-typing

16
推荐指数
1
解决办法
6026
查看次数

Python:将变量注释为 TypedDict 的键

基本上是这个问题(尚未得到解答)的精炼版本。

我想声明的是,变量应该只采用TypedDict.

目前我正在定义一个单独的Literal类型来表示键,例如:

from typing import Literal, TypedDict


class MyTD(TypedDict):
    a: int
    b: int


mytd = MyTD(a=1, b=2)

key = "a"

mytd[key]  # error: TypedDict key must be a string literal; expected one of ('a', 'b')

MyTDKeyT = Literal["a", "b"]

typed_key: MyTDKeyT = "b"

mytd[typed_key]  # no error
Run Code Online (Sandbox Code Playgroud)

我希望能够Literal出于想要最小化重复代码的所有常见原因替换定义。

伪代码:

key: Keys[MyTD] = "a"
mytd[key]  # would be no error
not_key: Keys[MyTD] = "z"  # error
Run Code Online (Sandbox Code Playgroud)

有办法实现这一点吗?

为了澄清,鉴于 mypy 可以告诉我键类型需要是“a”或“b”的文字,我希望可能有一种不太容易出错的方法来将变量注释为该类型,而不是使用并排维护两个单独的键列表,一次在定义中TypedDict,一次在Literal …

python mypy python-typing

16
推荐指数
1
解决办法
2942
查看次数

如何让抽象数据类传递给mypy?

mypy v0.910 拒绝 Python 3.9 中的抽象数据类。这是最小的可重复示例:

from abc import ABC, abstractmethod
from dataclasses import dataclass

@dataclass
class Liquid(ABC):

    @abstractmethod
    def drip(self) -> None:
        pass
Run Code Online (Sandbox Code Playgroud)

这是错误消息:

$ mypy --python-version 3.9 so.py
so.py:4: error: Only concrete class can be given where "Type[Liquid]" is expected
Found 1 error in 1 file (checked 1 source file)
Run Code Online (Sandbox Code Playgroud)

如何让此代码通过mypy


笔记

我从mypy 问题 #5374中了解到,这是mypy中的一个错误,于 2018 年首次注意到,但仍未得到纠正。不过,我认为人们必须将mypy与抽象数据类一起使用,因此必须有一种解决方法或正确的方法来定义或注释该类。有什么推荐的?

错误消息的基础似乎是mypy假设任何类型的对象都Type可以实例化,但抽象类不能实例化。这似乎是错误,因为Type定义为表示类对象,而不一定是具体的类对象(即可以实例化的类对象)。

添加# type: ignore到包含的行 …

python abstract-base-class mypy python-dataclasses python-typing

16
推荐指数
2
解决办法
5552
查看次数