检查类型提示是否带注释的正确方法是什么?

问题描述 投票:0回答:2

Python 3.9 引入了

Annotated
类,它允许添加任意元数据来键入提示,例如,

class A:
    x: Annotated[int, "this is x"]

可以通过设置

include_extras
的新
get_type_hints
参数来获取带注释的类型提示:

>>> get_type_hints(A, include_extras=True)
{'x': typing.Annotated[int, 'this is x']}

并且可以通过类型提示的

__metadata__
属性来访问元数据本身。

>>> h = get_type_hints(A, include_extras=True)
>>> h["x"].__metadata__
('this is x',)

但是,我的问题是,测试类型提示是否is

Annotated
的正确方法是什么?也就是说,类似于:

if IS_ANNOTATED(h["x"]):
    # do something with the metadata

据我所知,没有记录的方法可以做到这一点,并且有几种可能的方法,但没有一种似乎是理想的。

type
Annotated
进行比较不起作用,因为类型提示不是
Annotated
的实例:

>>> type(h["x"])
typing._AnnotatedAlias

所以我们必须做:

if type(h["x"]) is _AnnotatedAlias:
    ...

但是,考虑到

_AnnotatedAlias
中的前导下划线,这可能需要使用实现细节。

另一个选项是直接检查

__metadata__
属性:

if hasattr(h["x"], "__metadata__"):
    ...

但这假设

__metadata__
属性是
Annotated
所独有的,在处理用户定义的类型提示时也不一定可以假设。

那么,是否有更好的方法来进行此测试?

python type-hinting python-typing
2个回答
10
投票

您可以使用

typing.get_origin
https://docs.python.org/3/library/typing.html#typing.get_origin)来做到这一点:

assert typing.get_origin(Annotated[int, 0]) is Annotated

assert typing.get_args(Annotated[int, 0]) == (int, 0)

8
投票

这个怎么样?

from typing import Annotated, Any
annot_type = type(Annotated[int, 'spam'])


def is_annotated(hint: Any, annot_type=annot_type) -> bool:
    return (type(hint) is annot_type) and hasattr(hint, '__metadata__')

或者,使用新的 PEP 647

from typing import Annotated, TypeGuard, Any
annot_type = type(Annotated[int, 'spam'])


def is_annotated(hint: Any, annot_type=annot_type) -> TypeGuard[annot_type]:
    return (type(hint) is annot_type) and hasattr(hint, '__metadata__')

此解决方案避免了直接使用任何实现细节。为了安全起见,我在其中添加了额外的

hasattr(hint, '__metadata__')
测试。

此解决方案的讨论

有趣的是,这个解决方案似乎与 Python 目前在

inspect
模块中实现多个功能的方式非常相似。
inspect.isfunction
目前的实现如下:

# inspect.py

# -- snip --

import types

# -- snip --

def isfunction(object):
    """Return true if the object is a user-defined function.
    Function objects provide these attributes:
        __doc__         documentation string
        __name__        name with which this function was defined
        __code__        code object containing compiled function bytecode
        __defaults__    tuple of any default values for arguments
        __globals__     global namespace in which this function was defined
        __annotations__ dict of parameter annotations
        __kwdefaults__  dict of keyword only parameters with defaults"""
    return isinstance(object, types.FunctionType)

那么你去

types
模块查找
FunctionType
的定义,你会发现它的定义如下:

# types.py

"""
Define names for built-in types that aren't directly accessible as a builtin.
"""

# -- snip --

def _f(): pass
FunctionType = type(_f)

当然,因为

function
对象的确切性质取决于Python在C级别的实现细节。

© www.soinside.com 2019 - 2024. All rights reserved.