Python 类型检查协议和描述符

问题描述 投票:0回答:1

当涉及描述符时,我观察到关于

typing.Protocol
的行为,但我不太完全理解。考虑以下代码:

import typing as t

T = t.TypeVar('T')


class MyDescriptor(t.Generic[T]):

    def __set_name__(self, owner, name):
        self.name = name

    def __set__(self, instance, value: T):
        instance.__dict__[self.name] = value

    def __get__(self, instance, owner) -> T:
        return instance.__dict__[self.name]


class Named(t.Protocol):

    first_name: str


class Person:

    first_name = MyDescriptor[str]()
    age: int

    def __init__(self):
        self.first_name = 'John'


def greet(obj: Named):
    print(f'Hello {obj.first_name}')


person = Person()
greet(person)

Person
是否隐式实现
Named
协议? 根据 mypy 的说法,事实并非如此

error: Argument 1 to "greet" has incompatible type "Person"; expected "Named"
note: Following member(s) of "Person" have conflicts:
note:     first_name: expected "str", got "MyDescriptor[str]"

我想这是因为 mypy 很快就得出结论,

str
MyDescriptor[str]
只是两种不同的类型。很公平。

但是,使用普通的

str
表示
first_name
或将其包装在获取和设置
str
的描述符中只是一个实现细节。这里的鸭子打字告诉我,我们使用
first_name
(界面)的方式不会改变。

换句话说,

Person
实现了
Named

顺便说一句,PyCharm 的类型检查器在这种特殊情况下不会抱怨(尽管我不确定这是设计使然还是偶然)。

按照

typing.Protocol
的预期用途,我的理解有错吗?

python python-typing mypy python-descriptors
1个回答
1
投票

我正在努力寻找它的参考资料,但我认为 MyPy 在描述符的一些更详细的细节上有点困难(你可以理解为什么,那里有一些神奇的东西)。我认为这里的解决方法就是使用

typing.cast
:

import typing as t

T = t.TypeVar('T')


class MyDescriptor(t.Generic[T]):
    def __set_name__(self, owner, name: str) -> None:
        self.name = name

    def __set__(self, instance, value: T) -> None:
        instance.__dict__[self.name] = value

    def __get__(self, instance, owner) -> T:
        name = instance.__dict__[self.name]
        return t.cast(T, name)


class Named(t.Protocol):
    first_name: str


class Person:
    first_name = t.cast(str, MyDescriptor[str]())
    age: int

    def __init__(self) -> None:
        self.first_name = 'John'


def greet(obj: Named) -> None:
    print(f'Hello {obj.first_name}')


person = Person()
greet(person)

通过了MyPy

© www.soinside.com 2019 - 2024. All rights reserved.