如何为类型参数设置 python 类型
How to setup python typing for a type argument
我想为作为参数传递的 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
传递到类型,我们希望根据该类型构建所选 class 的对象。此场景的正确类型是什么(而不是代码示例中使用的 Any
)。
你的例子很有活力。由于 Python 类型注释的主要目的是支持 static 分析,因此它们在这里可以为我们提供的内容是有限的。但是,我确实认为我们可以做得比 Any
!
(注意:为了防止混淆,我会将您代码段中名为 object
的变量称为 my_object
,当我使用 object
时,我的意思是 Python类型)。
使用typing.Type
由于您提到提供的字典中的值应该是可以启动的类型,因此合理的类型注释应该是 Dict[str, Type[object]]
或 Dict[str, Type]
,后者等同于 Dict[str, Type[Any]]
。第一个是最严格的,它将限制类型检查器允许您对 my_object
变量执行的操作。第二个只比普通 Any
稍微好一点,尽管它确实会在调用者不小心提供实例而不是类型时警告调用者。
或typing.Callable?
根据您提供的代码段,使用字典值的唯一方法是创建一个新实例。类型并不是唯一可以做到这一点的东西。实际上,任何采用零参数和 returns 对象的可调用对象(一个普通函数,一个 class 方法)也足够了。因此,使用 Callable[[], object]
将为函数的调用者提供更大的灵活性,并且还会在传递没有零参数构造函数的类型时发出警告。我更喜欢这个而不是 Type
,因为它看起来更安全,而且更符合 Python 鸭子打字的精神。
有比普通对象更好的东西吗?
这两种解决方案都有一个局限性,即在您的函数内部,我们对 my_object
的类型一无所知。在一般情况下,每次我们执行对象类型不支持的操作时,我们都会 'forced' 进行 isinstance
检查。但是,也许在您的用例中,单个字典不应该只包含任何类型。可能是:
- 所有类型都有一个共同的基数 class,比如
MyBase
。
- 所有类型都应该支持一组通用操作(方法或属性)。
在情况 (1) 中,我们可以将 object
类型替换为 MyBase
类型。类型检查器应该能够确保 MyBase
支持的操作可以安全使用,并在提供的字典包含其他类型时发出警告。
对于情况(2),typing.Protocol
可以帮到我们。您可以使用所需的操作定义自定义协议,并将其添加到类型注释中:
class MyCustomProto(Protocol):
foo: int
def bar(self) -> str: ...
现在类型检查器应该知道 my_object
应该具有整数属性
foo
和一个方法 bar
即 returns 一个字符串。请注意,Protocol
已添加到 Python 3.8,并且可通过 typing-extensions 包用于以前的版本。
Dict 与 typing.Mapping
与你的问题没有直接关系,但如果你只是用字典来阅读,你应该考虑将Dict
替换为Mapping
类型。这允许调用者提供除 dict subclasses 之外的任何映射类型(在实践中不太可能发生)。但更重要的是,它会在不小心修改提供的映射时发出警告,这会导致很难发现错误。因此,我的最终建议是 Mapping[str, Callable[[], <X>]
,其中 <X>
是 object
、MyCustomProto
或 MyBase
.
之一
我想为作为参数传递的 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
传递到类型,我们希望根据该类型构建所选 class 的对象。此场景的正确类型是什么(而不是代码示例中使用的 Any
)。
你的例子很有活力。由于 Python 类型注释的主要目的是支持 static 分析,因此它们在这里可以为我们提供的内容是有限的。但是,我确实认为我们可以做得比 Any
!
(注意:为了防止混淆,我会将您代码段中名为 object
的变量称为 my_object
,当我使用 object
时,我的意思是 Python类型)。
使用typing.Type
由于您提到提供的字典中的值应该是可以启动的类型,因此合理的类型注释应该是 Dict[str, Type[object]]
或 Dict[str, Type]
,后者等同于 Dict[str, Type[Any]]
。第一个是最严格的,它将限制类型检查器允许您对 my_object
变量执行的操作。第二个只比普通 Any
稍微好一点,尽管它确实会在调用者不小心提供实例而不是类型时警告调用者。
或typing.Callable?
根据您提供的代码段,使用字典值的唯一方法是创建一个新实例。类型并不是唯一可以做到这一点的东西。实际上,任何采用零参数和 returns 对象的可调用对象(一个普通函数,一个 class 方法)也足够了。因此,使用 Callable[[], object]
将为函数的调用者提供更大的灵活性,并且还会在传递没有零参数构造函数的类型时发出警告。我更喜欢这个而不是 Type
,因为它看起来更安全,而且更符合 Python 鸭子打字的精神。
有比普通对象更好的东西吗?
这两种解决方案都有一个局限性,即在您的函数内部,我们对 my_object
的类型一无所知。在一般情况下,每次我们执行对象类型不支持的操作时,我们都会 'forced' 进行 isinstance
检查。但是,也许在您的用例中,单个字典不应该只包含任何类型。可能是:
- 所有类型都有一个共同的基数 class,比如
MyBase
。 - 所有类型都应该支持一组通用操作(方法或属性)。
在情况 (1) 中,我们可以将 object
类型替换为 MyBase
类型。类型检查器应该能够确保 MyBase
支持的操作可以安全使用,并在提供的字典包含其他类型时发出警告。
对于情况(2),typing.Protocol
可以帮到我们。您可以使用所需的操作定义自定义协议,并将其添加到类型注释中:
class MyCustomProto(Protocol):
foo: int
def bar(self) -> str: ...
现在类型检查器应该知道 my_object
应该具有整数属性
foo
和一个方法 bar
即 returns 一个字符串。请注意,Protocol
已添加到 Python 3.8,并且可通过 typing-extensions 包用于以前的版本。
Dict 与 typing.Mapping
与你的问题没有直接关系,但如果你只是用字典来阅读,你应该考虑将Dict
替换为Mapping
类型。这允许调用者提供除 dict subclasses 之外的任何映射类型(在实践中不太可能发生)。但更重要的是,它会在不小心修改提供的映射时发出警告,这会导致很难发现错误。因此,我的最终建议是 Mapping[str, Callable[[], <X>]
,其中 <X>
是 object
、MyCustomProto
或 MyBase
.