首页 > 解决方案 > 如何为类型参数设置 python 类型

问题描述

我想为作为参数传递的 Python 类型正确添加类型。例如,让我们假设我们想为以下函数添加类型:

def do_something_based_on_types(
    ...
    type_name: str,
    str_to_type_mapping: Dict[str, Any], # what instead of Any?
):
    ...
    object = str_to_type_mapping[type_name]()
    ...

我们希望将映射从str类型传递到我们希望构造选定类的对象的类型。这种情况下的正确类型是什么(而不是Any在代码示例中使用)。

标签: pythontypingdynamic-typing

解决方案


你的例子是非常动态的。由于 Python 类型注解的主要目的是支持静态分析,因此它们在这里为我们提供的功能是有限的。但是,我确实认为我们可以做得更好Any

(注意:为防止混淆,我将在您的代码段中引用名为 的变量objectmy_object当我使用 时object,我指的是 Python 类型)。

使用打字。类型

由于您提到提供的字典中的值应该是可以启动的类型,因此合理的类型注释将是Dict[str, Type[object]]or Dict[str, Type],其中后者等效于Dict[str, Type[Any]]. 第一个是最严格的,它将限制类型检查器允许您对my_object变量执行的操作。第二个只比 plain 稍微好一点Any,尽管它会在调用者意外提供实例而不是类型时警告调用者。

打字。可调用?

从您提供的片段中,使用字典值的唯一方法是创建一个新实例。类型不是唯一可以做到这一点的东西。实际上,任何接受零参数并返回对象的可调用(普通函数,类方法)也足够了。因此 usingCallable[[], object]将为函数的调用者提供更大的灵活性,并且还会在传递没有零参数构造函数的类型时发出警告。我更喜欢这个,Type因为它看起来更安全,更符合 Python 的鸭式打字精神。

比普通物体更好的东西?

这两种解决方案都有一个限制,即在您的函数内部,我们对my_object. 在一般情况下,isinstance每次我们执行对象类型不支持的操作时,我们都“被迫”进行检查。但是,也许在您的用例中,单个字典不应该包含任何类型。可能是这样的:

  1. 所有类型都有一个共同的基类,比如MyBase.
  2. 所有类型都应该支持一组通用的操作(方法或属性)。

在情况 (1) 中,我们可以将objecttype 替换为 type MyBase。类型检查器应该能够MyBase安全地使用所支持的操作,并在提供的字典包含其他类型时发出警告。

对于情况(2),typing.Protocol可以帮助我们。您可以使用所需的操作定义自定义协议,并将其添加到类型注释中:

class MyCustomProto(Protocol):
    foo: int
    def bar(self) -> str: ...

现在类型检查器应该知道my_object应该有整数属性 foobar返回字符串的方法。请注意,它已Protocol添加到 Python 3.8,并且可通过typing-extensions包用于以前的版本。

字典与打字。映射

和你的问题没有直接关系,但是如果你只用字典来阅读,你应该考虑用Dict类型替换Mapping。这允许调用者提供除 dict 子类之外的任何映射类型(在实践中不太可能发生)。但更重要的是,它会在您意外修改提供的映射时发出警告,这可能会导致难以发现错误。因此,我的最终建议是Mapping[str, Callable[[], <X>]<X>成为或中object的一个。MyCustomProtoMyBase


推荐阅读