如何使 "isinstance" on Protocols 还包括函数签名和数据类型?
How can I make "isinstance" on Protocols also include function signatures and data-types?
以下代码定义了一个简单的协议和一个 class,almost 实现了该协议。唯一的区别是 run()
方法在协议中接受一个参数,但它不是以这种方式实现的。
然而,isinstance()
检查 returns 真,这是出乎意料的。
据我了解PEP-544,此应该 有效。虽然关于检查函数签名还很不清楚。
在 ORDER
成员中使用错误的数据类型时会发生同样的问题(f.ex。在协议中将其更改为 str
)。
我知道类型提示只是……好吧……“提示”,不会在运行时强制执行。
但是,在我的应用程序中,确保某些 classes 遵循定义的协议以获得更清晰的错误消息会很有用。它使用的是插件架构,在加载插件后,如果该插件遵循所需的协议,则进行快速“健全性检查”将很有用,如果不遵循,则尽早提供有用的错误消息而不是导致异常稍后在下游。
from typing_extensions import Protocol, runtime_checkable
@runtime_checkable
class PFoo(Protocol):
ORDER: int
def run(self, a: int) -> None:
...
class Hello:
ORDER = 10
def run(self) -> None:
print(1)
# Returns "True" evn though the signature of "run()" doesn't match
print(isinstance(Hello(), PFoo))
这是 typing
module 中 runtime_checkable
函数的文档字符串:
"""Mark a protocol class as a runtime protocol.
Such protocol can be used with isinstance() and issubclass().
Raise TypeError if applied to a non-protocol class.
This allows a simple-minded structural check very similar to
one trick ponies in collections.abc such as Iterable.
For example::
@runtime_checkable
class Closable(Protocol):
def close(self): ...
assert isinstance(open('/some/file'), Closable)
Warning: this will check only the presence of the required methods,
not their type signatures!
"""
如果你想比较函数的类型注释,你可以检查函数的 __annotations__
属性(如果它是一个启用了 from __future__ import annotations
的模块,最好使用 typing.get_type_hints
on the function rather than examining the __annotations__
attribute directly). And if you're looking for an exact signature match, you can use the signature
function from the inspect
module — docs here。注意:我认为使用 inspect.signature
可能会产生一些运行时性能成本(但话又说回来,所有运行时类型检查也是如此)。
以下代码定义了一个简单的协议和一个 class,almost 实现了该协议。唯一的区别是 run()
方法在协议中接受一个参数,但它不是以这种方式实现的。
然而,isinstance()
检查 returns 真,这是出乎意料的。
据我了解PEP-544,此应该 有效。虽然关于检查函数签名还很不清楚。
在 ORDER
成员中使用错误的数据类型时会发生同样的问题(f.ex。在协议中将其更改为 str
)。
我知道类型提示只是……好吧……“提示”,不会在运行时强制执行。
但是,在我的应用程序中,确保某些 classes 遵循定义的协议以获得更清晰的错误消息会很有用。它使用的是插件架构,在加载插件后,如果该插件遵循所需的协议,则进行快速“健全性检查”将很有用,如果不遵循,则尽早提供有用的错误消息而不是导致异常稍后在下游。
from typing_extensions import Protocol, runtime_checkable
@runtime_checkable
class PFoo(Protocol):
ORDER: int
def run(self, a: int) -> None:
...
class Hello:
ORDER = 10
def run(self) -> None:
print(1)
# Returns "True" evn though the signature of "run()" doesn't match
print(isinstance(Hello(), PFoo))
这是 typing
module 中 runtime_checkable
函数的文档字符串:
"""Mark a protocol class as a runtime protocol.
Such protocol can be used with isinstance() and issubclass().
Raise TypeError if applied to a non-protocol class.
This allows a simple-minded structural check very similar to
one trick ponies in collections.abc such as Iterable.
For example::
@runtime_checkable
class Closable(Protocol):
def close(self): ...
assert isinstance(open('/some/file'), Closable)
Warning: this will check only the presence of the required methods,
not their type signatures!
"""
如果你想比较函数的类型注释,你可以检查函数的 __annotations__
属性(如果它是一个启用了 from __future__ import annotations
的模块,最好使用 typing.get_type_hints
on the function rather than examining the __annotations__
attribute directly). And if you're looking for an exact signature match, you can use the signature
function from the inspect
module — docs here。注意:我认为使用 inspect.signature
可能会产生一些运行时性能成本(但话又说回来,所有运行时类型检查也是如此)。