使用PEP 563检查签名

时间:2018-11-20 10:29:28

标签: python python-3.x types python-3.7 static-typing

以下代码:

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对象?

3 个答案:

答案 0 :(得分:3)

除非需要,否则使用PEP 536的目的是评估注释。签名仅报告注释。

如果出于您的目的需要解析注释,则必须自己解决。 PEP 536告诉documents how you do this

  

对于使用类型提示的代码,typing.get_type_hints(obj, globalns=None, localns=None)函数可以从其字符串形式正确地返回表达式。

     

[...]

     

对于将注释用于其他目的的代码,常规的eval(ann,globals,locals)调用足以解析注释。

您甚至可以在获得签名之前使用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,这样做也是安全的。

答案 1 :(得分:1)

首先,让我们运行另一个示例:

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。一个给您实际的课程,另一个给您typing.ForwardRef

答案 2 :(得分:0)

您必须实际使用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)

或者,您可以在调用__annotations__之前修改inspect.signature(obj),但是我发现这太难了,因为我需要涵盖多种不同的情况。

@Martijn Pieters的答案缺少有关typing.get_type_hints的一个细节:

  

如有必要,如果设置的默认值等于无,则添加Optional [t]

示例:

# 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)