具有两个用例的类型提示功能

Posted

技术标签:

【中文标题】具有两个用例的类型提示功能【英文标题】:Type hinting function with two use cases 【发布时间】:2021-09-20 08:43:00 【问题描述】:

我正在尝试完全键入提示一个函数,该函数确保一个元素在给定的字典中,然后检查元素类型是否是用户期望的。我的初始实现工作正常,如下所示

T = TypeVar("T")

def check_and_validate_element_in_dict(
    element_name: str, dictionary: Dict[str, Any], element_type: Type[T]
) -> T:
    assert element_name in dictionary
    element = dictionary[element_name]
    assert isinstance(element, element_type)
    return element

它允许我替换它

assert "key1" in _dict
key1 = _dict["key1"]
assert isinstance(key1, type1)

assert "key2" in _dict
key2 = _dict["key2"]
assert isinstance(key2, type2)

有了这个

key1 = check_and_validate_element_in_dict("key1", _dict, type1)
key2 = check_and_validate_element_in_dict("key2", _dict, type2)

现在,这仅适用于要测试的元素类型只有一种,例如 intstr 等。 我还希望能够在我的函数中测试多种不同的类型,比如

isinstance(element, (int, dict))
isinstance(element, (float, type(None)))

这里的问题是类型提示函数,以使其理解如果 element_type 是单个值 T,则返回值是 T,但如果 element_type 是例如其中之一TU 两种类型,返回值将是TU

我想这是可能的,但由于我还是类型提示领域的新手,我需要一些帮助!

编辑:

我尝试让函数支持单一类型或两种不同类型的元组作为基本情况,因此我将element_type 更新为

element_type: Union[Type[T], Tuple[Type[T], Type[T]]]

现在return element 语句被mypy 标记为错误:

Returning Any from function declared to return "T"

这也提出了一个问题:我是否需要将每个不同的输入类型指示为新的TypeVar?在这种情况下,element_type 定义变为

# using U = TypeVar("U")
def ...(..., element_type: Union[Type[T], Tuple[Type[T], Type[U]]]) -> Union[T, U]:

在这种情况下,问题一直存在

Returning Any from function declared to return "T"

【问题讨论】:

首先要尝试的是element_type: Type[T] | Tuple[Type[T]] - 当你这样做时会发生什么? @kaya3 你对Type[T] | Tuple[Type[T]] 到底是什么意思?尤其是|,还有Tuple[Type[T]]表示一个元组里面只有一个类型 @HitLuca -- 在 Python 3.10 中添加了 | 运算符,以便为 typing.Union 启用更简单的注释。 Python 3.10 中的int | float 与早期版本中的Union[int, float] 相同。关于tuple 点,我同意——我认为这不是一个完美的解决方案。 对不起,我的意思是Tuple[Type[T], ...] 应该是一个任意多个元素的元组。 哦,我不知道|操作,对不起,我使用的是python3.7。我其实也不知道你可以在 Tuple[Type[T], ...] 之类的东西中使用省略号! 【参考方案1】:

您可以使用typing.overload,它允许您为一个函数注册多个不同的签名。用@overload 装饰的函数在运行时会被python 忽略,因此您可以通过在函数体中添加省略号... 将这些函数体留空。这些实现仅适用于类型检查器——您必须确保至少有一个没有用重载修饰的函数的“真实”实现。

from typing import TypeVar, overload, Any, Union, Dict, Type, Tuple

t0 = TypeVar('t0')
t1 = TypeVar('t1')


@overload
def check_and_validate_element_in_dict(
        element_name: str,
        dictionary: Dict[str, Any],
        element_type: Type[t0]
) -> t0:
    """
    Signature of the function if exactly one type
    is supplied to argument element_type
    """
    ...


@overload
def check_and_validate_element_in_dict(
        element_name: str,
        dictionary: Dict[str, Any],
        element_type: Tuple[Type[t0], Type[t1]]
) -> Union[t0, t1]:
    """
    Signature of the function if a tuple of exactly two types
    is supplied to argument element_type
    """     
    ...


def check_and_validate_element_in_dict(
        element_name: str,
        dictionary: Dict[str, Any],
        element_type: Any
) -> Any:
    """Concrete implementation of the function"""
    
    assert element_name in dictionary
    element = dictionary[element_name]
    assert isinstance(element, element_type)
    return element

然而,这感觉像是一个非常不完美的解决方案,因为它没有为传递给element_type 参数的任意长度的元组提供解决方案。仅当您知道元组的长度将是(例如)2、3 或 4 之一时,它才有效——然后您可以为每种情况提供重载实现。如果有人能想到更好的解决方案,肯定会感兴趣。

【讨论】:

我怀疑有这样的事情!几年前我读过它,但不知道如何寻找它,绝对是我在寻找的东西(你连续第二天帮助我看起来像,我想我需要雇用你作为个人类型提示导师!) 我同意这不是最好的方法,但我想我可以使用它,因为目前我最多有 3 种不同的类型检查要做 啊,太棒了——再一次,很高兴我能帮上忙!

以上是关于具有两个用例的类型提示功能的主要内容,如果未能解决你的问题,请参考以下文章

自动化测试用例的原子性#yyds干货盘点#

软件测试:测试用例的设计思想

思维导图编写测试用例的两种格式

用例之间的可视化关系

为什么我们需要将一个用例分成两个或多个用例?

pytest -allure标记用例级别severity