抽象类/接口中描述符的类型提示

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

我想要一个具有由描述符类定义的自定义属性的类和一个隐藏实现细节的接口。

我的代码目前看起来像这样。它有效,类型检查器不会抱怨,但由于从

cast
Ten
,它看起来很难看。如果我希望描述符为
int
,中间函数
ten
也会使事情变得复杂。
Generic

是否有可能在不使用像这样的魔术技巧的情况下实现某种程度的类型检查并且类型检查器不会出现错误?

python python-typing
1个回答
0
投票

它将返回的东西。如果您输入 from typing import cast class Ten: def __get__(self, obj, objtype=None) -> int: return 10 def __set__(self, obj, value: int) -> None: pass # just to make Ten "writeable" def ten() -> int: return cast(int, Ten()) # <-- this is ugly class ABase: x: int class A(ABase): x: int = ten() def fn(value: ABase) -> int: return value.x a = A() print(fn(a)) 作为通用描述符,那么 mypy(至少)将理解对属性的访问将导致描述符返回的结果。它甚至会理解描述符如何根据是在类上还是在实例上访问描述符来返回不同的结果。例如。

ABase.x

如果您对此文件调用 
from typing import overload, Callable, Generic, TypeVar T = TypeVar('T') Value = TypeVar('Value') ClassValue = TypeVar('ClassValue') class GenericNonDataDescriptor(Generic[T, Value, ClassValue]): def __init__( self, inst_func: Callable[[T], Value], class_func: Callable[[type[T]], ClassValue] ) -> None: self.inst_func = inst_func self.class_func = class_func @overload def __get__(self, instance: T, owner: type[T]) -> Value: ... @overload def __get__(self, instance: None, owner: type[T]) -> ClassValue: ... def __get__(self, instance: T | None, owner: type[T]) -> Value | ClassValue: print(f'{instance=} and {owner=}') if instance is None: return self.class_func(owner) else: return self.inst_func(instance) def ten(instance: 'Foo') -> int: return 10 def ten_for_class(cls: type['Foo']) -> str: return 'called on class' class MyClass: x = GenericNonDataDescriptor(ten, ten_for_class) reveal_type(MyClass().x) # line 33 reveal_type(MyClass.x) # line 34

,您将得到:

mypy

替代方法

由于 mypy 不喜欢交换实际类型和描述符提供访问的类型,因此您将无法干净地进行代码示例类型检查。因此,除了将

file.py:33: note: Revealed type is "builtins.int" file.py:34: note: Revealed type is "builtins.str" Success: no issues found in 1 source file

属性的类型更改为“

x
”的并集之外,您实际上还能做什么?
一个答案是使用属性。 Mypy 和其他类型检查器对属性有更好的理解,即使它们是作为(数据)描述符实现的。使用属性(和 

int | Descriptor[int]

模块),您可以获得几乎相同的接口、易于阅读的类型以及未定义所需接口的类的运行时保护。最后一点很重要,如果一个类忘记“覆盖”

abc
,那么类型检查器不会抱怨,并且该属性在运行时不会出现。
x

关键的区别在于,通过类访问属性不会调用该属性,而是返回属性本身。 mypy 显示的是:

from abc import ABC, abstractmethod class Base(ABC): @abstractmethod def _x(self) -> int: ... @property def x(self) -> int: return self._x() class Foo(Base): def _x(self) -> int: return 10 reveal_type(Foo().x) # line 15 reveal_type(Foo.x) # line 16

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