【问题标题】:What Python type annotation to represent generic type class什么 Python 类型注解来表示泛型类型类
【发布时间】:2019-11-06 05:48:37
【问题描述】:

在我的 Python 3.7.4 代码中,我具有以下功能。

def is_dict(klass: ???) -> bool:
    return klass.__origin__ == dict

我正在努力为klass 参数获取正确的类型注释。这不是 type,因为 mypy 抱怨。

错误:“type”没有属性“__origin__

我很茫然。什么是正确的注释,是否有任何关于它的好的文档?

以下是如何使用此函数的示例:

>>> is_dict(typing.Dict[str, int])
True
>>> is_dict(typing.List[str])
False

【问题讨论】:

标签: python mypy python-typing


【解决方案1】:

如果您想按照 cmets 中的建议使用 GenericMeta 之类的东西,或者尝试创建自定义 protocol 来定义适当的 __origin__ 属性,您首先需要更新在typeshed 中键入模块来定义这些属性。

不过,目前我建议只使用Type[Any]Type[object]

from typing import Type, Any

# If you want to make minimal modifications to your code, make
# 'klass' dynamically typed.
def is_dict_1(klass: Type[Any]) -> bool:
    return klass.__origin__ == dict

# If you want to make your code robust, assume nothing about 'klass'
# except that it's some sort of type and verify the attribute you're
# about to use exists.
def is_dict_2(klass: Type[object]) -> bool:
    return getattr(klass, '__origin__', None) == dict

如果您因为尝试创建某种序列化/反序列化库而专门尝试直接操作类型提示表达式,您也可以尝试查看诸如 pydantic 之类的库的源代码以获取灵感。

更广泛地说,我还建议您探索尽可能减少代码中将类型提示表达式作为运行时实体操作的位置的可能性。 Python 类型化生态系统在很大程度上旨在将静态世界和运行时世界分开,因此混合这两个世界的机制使用起来并不那么方便,并且并不总是向后兼容。例如,自 Python 3.5 首次发布以来,类型库的内部结构已经发生了多次变化。

【讨论】:

    【解决方案2】:

    我已经编辑了答案:

    import typing
    
    def is_dict(klass: typing.GenericMeta) -> bool:
        return klass.__origin__ == typing.Dict
    
    if __name__ == "__main__":
        print(is_dict(typing.Dict[str, int]))
        print(is_dict(typing.List[str]))
    

    或者:

    def is_dict(klass: typing.GenericMeta) -> bool:
            return klass.__orig_bases__[0] == dict
    

    【讨论】:

    • 谢谢,虽然这不是我所追求的。我在原始帖子中添加了一个示例,希望能更好地说明我所追求的。
    • 我认为这应该为您的需要提供一个基础。
    • 如果您只想使用dict 或内置,您可以参考第二个示例
    • 这仍然让 mypy 发牢骚error: "GenericMeta" has no attribute "__origin__"
    • Python 3.7+ 没有typing.GenericMeta
    【解决方案3】:

    我的理解是在这种情况下你应该使用cls: type,因为从类型级别的角度来看,它会接受strint之类的类型,而且还接受typing.Dicttyping.List等类型。

    您可以通过使用hasattr / getattr 轻松绕过mypy 的投诉“type”没有属性“__origin__”。在类型检查和运行时行为方面,以下内容应该适用于 Python 3.6+。

    from typing import Dict, List
    
    
    def is_dict(cls: type) -> bool:
        if cls is dict:
            # Add this depending on whether you want to accept plain `dict`
            return True
        elif hasattr(cls, "__origin__"):
            origin = getattr(cls, "__origin__")
            # In Python 3.7+ origin will be `dict`, in Python 3.6 it is `Dict`
            return origin == dict or origin == Dict
        return False
    
    
    assert not is_dict(int)
    assert not is_dict(str)
    assert not is_dict(List[int])
    assert is_dict(dict)
    assert is_dict(Dict[int, int])
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-25
      • 1970-01-01
      • 1970-01-01
      • 2019-02-14
      • 2022-01-18
      • 2012-04-20
      • 2020-11-06
      • 2023-03-06
      相关资源
      最近更新 更多