是否可以键入提示类属性的附加属性?
我有一个类属性
Outer.inner
,它可以是符合协议InnerProto
的类的任何实例。
#### I do not control this code #######
from typing import Protocol
class InnerProto(Protocol):
arg: int
class OuterProto(Protocol):
inner: InnerProto
Inner
类符合协议InnerProto
,但它还有一个附加属性(据我所知,这并不违反协议一致性)
class Outer:
def __init__(self, inner: InnerProto):
self.inner: InnerProto = inner
class Inner:
def __init__(self, arg: int):
self.arg: int = arg # this is part of the protocol
self.arg2: int = arg # this is not
Pycharm (v2021.2) 检查将属性
arg2
突出显示为未解析的属性。
def test_type_hint_example():
obj = Outer(Inner(1))
assert obj.inner.arg == 1
assert obj.inner.arg2 == 1 # inspection highlights arg2 as "unresolved attribute"
是否可以输入提示以便检查识别 arg2 是什么?
通常我会倾向于更改
Outer.inner
的类型提示,但在这种情况下,这将是一个错误。 inner
可以是任何符合 InnerProto
的类。
我的第二个想法是覆盖实例属性的类型提示。 这种类型的技巧已经适用于非属性对象,但在这种情况下,它会导致不同的检查错误:“非自身属性无法进行类型提示”
def test_option1():
obj = Outer(Inner(1))
obj.inner: Inner # fixes inspection for arg2, but "Non-self attribute could not be type hinted" occurs
assert obj.inner.arg == 1
assert obj.inner.arg2 == 1
还有哪些其他选择?
我认为这里的问题是代码不是类型安全的。
您的
Outer.__init__
功能是:
class Outer:
def __init__(self, inner: InnerProto):
self.inner = inner
# <-- snip -->
您已用
inner
注释了 InnerProto
参数。这意味着 Outer.inner
属性可以是 any 类型,只要该类型符合 InnerProto
协议即可。
根据您的说法,这是正确的类型提示:
通常我会倾向于更改
的类型提示,但在这种情况下,这将是一个错误。Outer.inner
可以是任何符合inner
的类。InnerProto
如果是这种情况,则静态类型检查器无法验证
Outer.inner
是否具有 InnerProto
中未指定的任何属性或方法。这就是类型检查器在您的 test_type_hint_example
函数中引发错误的原因:您已经告诉类型检查器 Outer.inner
可以是任何类型,只要该类型符合 InnerProto
即可,但是 InnerProto
没有提及 arg2
属性,并且并非所有 python 类型都有 arg2
属性,因此当您访问 AttribiteError
时,您有可能会得到 Outer.inner.arg2
。
如果您确定,在这个特定函数的上下文中,
Outer.inner
将是Inner
类型,因此可以保证具有arg2
属性——尽管事实上在大多数情况下, Outer.inner
“可以是任何符合 InnerProto
的类”——那么你可以像这样使用 typing.cast
:
from typing import cast
def test_type_hint():
obj = Outer(Inner(1))
inner = cast(Inner, obj.inner)
assert inner.arg == 1
assert inner.arg2 == 1
然而,实际上,如果您确信可以保证
Outer.inner
具有arg2
属性,那么很可能表明您实际上对Outer.inner
有错误的类型提示(这很难告诉您正确的类型提示是什么,而无需查看更多代码库)。按照我上面刚刚演示的方式使用 typing.cast
通常只能用作最后的手段 - 它更多的是逃避类型检查器的方法,从长远来看,如果你的代码不是这样,这对你没有帮助类型安全。