【问题标题】:inspect.signature with PEP 563使用 PEP 563 检查.signature
【发布时间】:2019-04-22 18:40:31
【问题描述】:

以下代码:

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 类型完全相同的对象?

【问题讨论】:

  • PEP 的重点得到相同的对象...你必须评估 字符串使用exec 等。

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


【解决方案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

【讨论】:

  • 请务必注意,a 注释推迟到 typing.ForwardRef(这很不寻常)而不是 str(这是通常情况下)是Example 类子类typing.NamedTuple。因此,您会在此处看到typing-特定的延期。一般来说,激活from __future__ import annotations 会将注释推迟到简单的未评估字符串——这比推迟到更高级别的对象(如typing.ForwardRef)要明智得多,因为 PEP 563 的全部目的是减少与注释相关的空间和时间成本。 Annnnnyway。
  • 好的,楼上的评论是你偶尔偶然发现的有用的东西,所以如果@CecilCurry 同意我想编辑我的答案并将评论作为某种附录
【解决方案2】:

使用 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 未被使用,这样做也是安全的。

【讨论】:

  • 完美。谢谢。明天我将奖励赏金,因为出于某种原因,stackoverflow 不允许我在不等待 3 小时的情况下立即这样做。 :rolleyes:
【解决方案3】:

您必须实际使用 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 的细节:

如果设置了等于 None 的默认值,则添加 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)

【讨论】:

    猜你喜欢
    • 2020-10-28
    • 2016-08-03
    • 1970-01-01
    • 2013-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-14
    相关资源
    最近更新 更多