inspect.signature 与 PEP 563
inspect.signature with PEP 563
以下代码:
import inspect
from typing import NamedTuple
class Example(NamedTuple):
a: str
if __name__== "__main__":
signature: inspect.Signature = inspect.signature(Example)
print(signature)
输出:
(a: str)
但是当启用 PEP 563 – Postponed Evaluation of Annotations:
from __future__ import annotations
import inspect
from typing import NamedTuple
class Example(NamedTuple):
a: str
if __name__== "__main__":
signature: inspect.Signature = inspect.signature(Example)
print(signature)
输出为:
(a: 'str')
如何使用 PEP 563 获得类型 inspect.Signature
完全相同的对象?
首先,让我们运行另一个例子:
signature: inspect.Signature = inspect.signature(Example)
print(signature)
print(Example.__annotations__)
这会打印:
(a: str)
OrderedDict([('a', <class 'str'>)])
到目前为止一切顺利,我们已经 Signature
和我们预期的 __anotations__
。
现在让我们对第二个例子做同样的事情,它打印:
(a: 'str')
OrderedDict([('a', ForwardRef('str'))])
所以你在这里得到的相同 Signature
。一个给你实际的 class,另一个给你一个 typing.ForwardRef
到 class。
使用 PEP 536 的目的是 不 评估注释,除非需要。签名仅报告注释。
如果出于您的目的需要解析注释,您必须自己动手。 PEP 536 告诉 documents how you do this:
For code that uses type hints, the typing.get_type_hints(obj, globalns=None, localns=None)
function correctly evaluates expressions back from its string form.
[...]
For code which uses annotations for other purposes, a regular eval(ann, globals, locals) call is enough to resolve the annotation.
您甚至可以在获得签名之前使用 typing.get_type_hints()
function 分配回 __annotations__
:
import typing
Example.__new__.__annotations__ = typing.get_type_hints(Example.__new__)
signature: inspect.Signature = inspect.signature(Example)
即使 from __future__ import annotations
没有被使用,这样做也是安全的。
您必须实际使用 eval
才能获得相同的行为:
from __future__ import annotations
import inspect
from typing import NamedTuple
class Example(NamedTuple):
a: str
signature: inspect.Signature = inspect.signature(Example)
print(signature)
# extra bit
globalns = getattr(Example, '__globals__', {})
for param in list(signature.parameters.values()):
if isinstance(param.annotation, str):
param._annotation = eval(param.annotation, globalns)
print(signature)
您将获得:
(a: 'str')
(a: str)
或者您可以在调用 inspect.signature(obj)
之前修改 __annotations__
,但我发现它太难了,因为我需要涵盖多个不同的案例。
@Martijn Pieters 的回答遗漏了一个关于 typing.get_type_hints
的细节:
if necessary adds Optional[t] if a default value equal to None is set
示例:
# without imporing annotations from __future__
import inspect
import typing
def func(a: str=None): pass
print(inspect.signature(func))
func.__annotations__ = typing.get_type_hints(func)
print(inspect.signature(func))
您将获得:
(a: str = None)
(a: Union[str, NoneType] = None)
以下代码:
import inspect
from typing import NamedTuple
class Example(NamedTuple):
a: str
if __name__== "__main__":
signature: inspect.Signature = inspect.signature(Example)
print(signature)
输出:
(a: str)
但是当启用 PEP 563 – Postponed Evaluation of Annotations:
from __future__ import annotations
import inspect
from typing import NamedTuple
class Example(NamedTuple):
a: str
if __name__== "__main__":
signature: inspect.Signature = inspect.signature(Example)
print(signature)
输出为:
(a: 'str')
如何使用 PEP 563 获得类型 inspect.Signature
完全相同的对象?
首先,让我们运行另一个例子:
signature: inspect.Signature = inspect.signature(Example)
print(signature)
print(Example.__annotations__)
这会打印:
(a: str)
OrderedDict([('a', <class 'str'>)])
到目前为止一切顺利,我们已经 Signature
和我们预期的 __anotations__
。
现在让我们对第二个例子做同样的事情,它打印:
(a: 'str')
OrderedDict([('a', ForwardRef('str'))])
所以你在这里得到的相同 Signature
。一个给你实际的 class,另一个给你一个 typing.ForwardRef
到 class。
使用 PEP 536 的目的是 不 评估注释,除非需要。签名仅报告注释。
如果出于您的目的需要解析注释,您必须自己动手。 PEP 536 告诉 documents how you do this:
For code that uses type hints, the
typing.get_type_hints(obj, globalns=None, localns=None)
function correctly evaluates expressions back from its string form.[...]
For code which uses annotations for other purposes, a regular eval(ann, globals, locals) call is enough to resolve the annotation.
您甚至可以在获得签名之前使用 typing.get_type_hints()
function 分配回 __annotations__
:
import typing
Example.__new__.__annotations__ = typing.get_type_hints(Example.__new__)
signature: inspect.Signature = inspect.signature(Example)
即使 from __future__ import annotations
没有被使用,这样做也是安全的。
您必须实际使用 eval
才能获得相同的行为:
from __future__ import annotations
import inspect
from typing import NamedTuple
class Example(NamedTuple):
a: str
signature: inspect.Signature = inspect.signature(Example)
print(signature)
# extra bit
globalns = getattr(Example, '__globals__', {})
for param in list(signature.parameters.values()):
if isinstance(param.annotation, str):
param._annotation = eval(param.annotation, globalns)
print(signature)
您将获得:
(a: 'str')
(a: str)
或者您可以在调用 inspect.signature(obj)
之前修改 __annotations__
,但我发现它太难了,因为我需要涵盖多个不同的案例。
@Martijn Pieters 的回答遗漏了一个关于 typing.get_type_hints
的细节:
if necessary adds Optional[t] if a default value equal to None is set
示例:
# without imporing annotations from __future__
import inspect
import typing
def func(a: str=None): pass
print(inspect.signature(func))
func.__annotations__ = typing.get_type_hints(func)
print(inspect.signature(func))
您将获得:
(a: str = None)
(a: Union[str, NoneType] = None)