如何在python中定义通过ForwardRef延迟计算的类型别名(TypeAlias)?

How to define a type alias (TypeAlias) that is evaluated lazily via ForwardRef in python?

提问人:Zacharias030 提问时间:6/12/2023 最后编辑:Zacharias030 更新时间:6/12/2023 访问量:278

问:

我的目标是防止在使用类型别名时在运行时导入昂贵的模块。

如果没有别名,可以将昂贵的模块隐藏在后面,并使导入成为相应函数的本地:typing.TYPE_CHECKING

# utils.py -- module that must be very lightweight to import
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    import tensorflow as tf  # lot's of unwanted side-effects

def rarely_used_function(t: "tf.Tensor"):  # <-- note the lazily evaluated type hint
   import tensorflow as tf
   ...

在定义类型别名时,如何实现相同的效果?

# types.py  -- a collection of our own type aliases
from typing import TYPE_CHECKING, TypeAlias

if TYPE_CHECKING:
    import tensorflow as tf

MyTensorAlias = "tf.Tensor"  # <-- now this is just a string assignment and will not evaluate to a `ForwardRef`


# Notes of things that do not work:
# 1) annotating with TypeAlias
MyOtherTensorAlias: TypeAlias = "tf.Tensor"  # <-- still just a str and will cause runtime errors

# 2) Aliases behind `TYPE_CHECKING`
if TYPE_CHECKING:
    MyLazyTensorAlias = tf.Tensor  # <-- this is fine for static type checkers, but raises at runtime when `types has no member MyLazyTensorAlias` anymore.

可以使用 实现延迟评估,但是子类型不是我想要的(因为这样我需要强制转换所有用户,例如 .typing.NewType()Subtype(tf.Tensor(...))

编辑:

它在运行时失败的方式特定于新运算符。 考虑:|

Python 3.10.8 (main, Nov 24 2022, 08:09:04) [Clang 14.0.6 ]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.7.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: from typing import TypeAlias
   ...: Alias: TypeAlias = "Original"
   ...: def func(a: Alias | None):
   ...:     pass
   ...:
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[1], line 3
      1 from typing import TypeAlias
      2 Alias: TypeAlias = "Original"
----> 3 def func(a: Alias | None):
      4     pass

TypeError: unsupported operand type(s) for |: 'str' and 'NoneType'

In [2]: from typing import TypeAlias,Optional
   ...:
   ...: Alias: TypeAlias = "Original"
   ...: def func(a: Optional[Alias]):
   ...:     pass
   ...:

In [3]: class Original:
   ...:     pass
   ...:

In [4]: def func(a: Original | None):
   ...:     pass
   ...:
python 键入 type-alias

评论

0赞 mkrieger1 6/12/2023
如果您尝试或 会发生什么?MyTensorAlias = ForwardRef("tf.Tensor")MyTensorAlias = TypeAlias("tf.Tensor")
1赞 Jasmijn 6/12/2023
MyOtherTensorAlias: TypeAlias = "tf.Tensor"应该根据文档工作。你会得到什么样的运行时错误?(请附上示例代码+回溯)
0赞 Zacharias030 6/12/2023
@mkrieger1 : ,使用 Forward ref,它的行为就像我的下一个注释(不支持的操作数类型)TypeError: Cannot instantiate typing.TypeAlias
0赞 Zacharias030 6/12/2023
@Jasmijn 感谢您指出文档,我同意它应该有效。当类型别名与新语法(python 3.10.8)结合使用时,我的代码库会引发问题:我将使用更多示例编辑问题Type | Nonefrom typing import TypeAlias; Alias: TypeAlias = "Original"; def func(a: Alias | None): pass; # -----> TypeError: unsupported operand type(s) for |: 'str' and 'NoneType'
0赞 Zacharias030 6/12/2023
@Jasmijn看起来这个问题是在 bugs.python.org/issue45857 年提交的,文档现在在 docs.python.org/3/library/stdtypes.html#union-type 中明确警告

答:

0赞 Zacharias030 6/12/2023 #1

正如@Jasminj所指出的,应该可以工作,但请注意,文档指出类型联合 via 不能与正向引用 (https://docs.python.org/3/library/stdtypes.html#union-type 一起使用MyOtherTensorAlias: TypeAlias = "tf.Tensor"X | Y)

评论

1赞 dROOOze 6/13/2023
|没问题,你只需要把整个表达式放在一个字符串中。.更好的是,放在模块的顶部,你不需要在注释表达式中使用任何字符串。"MyOtherTensorAlias | None"from __future__ import annotations