python - 有没有办法在 python 中为带有类型提示的函数参数指定一系列有效值?
问题描述
我是 python 中类型提示的忠实粉丝,但是我很好奇是否有一种方法可以使用类型提示为给定参数指定一个有效的值范围。
我的想法是
from typing import *
def function(
number: Union[float, int],
fraction: Float[0.0, 1.0] = 0.5 # give a hint that this should be between 0 and 1,
):
return fraction * number
我可以想象人们可以通过断言来强制执行这一点,或者可以指定文档字符串中值的有效范围,但感觉像 Float[0.0, 1.0] 这样的东西看起来更优雅。
解决方案
Python 3.9 介绍typing.Annotated
:
In [75]: from typing import *
In [76]: from dataclasses import dataclass
In [77]: @dataclass
...: class ValueRange:
...: min: float
...: max: float
...:
In [78]: def function(
...: number: Union[float, int],
...: fraction: Annotated[float, ValueRange(0.0, 1.0)] = 0.5
...: ):
...: return fraction * number
...:
像任何其他类型提示一样,它不执行任何运行时检查:
In [79]: function(1, 2)
Out[79]: 2
但是,您可以实现自己的运行时检查。下面的代码只是一个示例,它并没有涵盖所有情况,并且可能对您的简单函数来说有点矫枉过正:
In [88]: import inspect
In [89]: @dataclass
...: class ValueRange:
...: min: float
...: max: float
...:
...: def validate_value(self, x):
...: if not (self.min <= x <= self.max):
...: raise ValueError(f'{x} must be in range [{self.min}, {self.max}]')
...:
In [90]: def check_annotated(func):
...: hints = get_type_hints(func, include_extras=True)
...: spec = inspect.getfullargspec(func)
...:
...: def wrapper(*args, **kwargs):
...: for idx, arg_name in enumerate(spec[0]):
...: hint = hints.get(arg_name)
...: validators = getattr(hint, '__metadata__', None)
...: if not validators:
...: continue
...: for validator in validators:
...: validator.validate_value(args[idx])
...:
...: return func(*args, **kwargs)
...: return wrapper
...:
...:
In [91]: @check_annotated
...: def function_2(
...: number: Union[float, int],
...: fraction: Annotated[float, ValueRange(0.0, 1.0)] = 0.5
...: ):
...: return fraction * number
...:
...:
In [92]: function_2(1, 2)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-92-c9345023c025> in <module>
----> 1 function_2(1, 2)
<ipython-input-90-01115cb628ba> in wrapper(*args, **kwargs)
10 continue
11 for validator in validators:
---> 12 validator.validate_value(args[idx])
13
14 return func(*args, **kwargs)
<ipython-input-87-7f4ac07379f9> in validate_value(self, x)
6 def validate_value(self, x):
7 if not (self.min <= x <= self.max):
----> 8 raise ValueError(f'{x} must be in range [{self.min}, {self.max}]')
9
ValueError: 2 must be in range [0.0, 1.0]
In [93]: function_2(1, 1)
Out[93]: 1
推荐阅读
- python - Python selenium 无法点击按钮
- javascript - 使用父类和没有ID的javascript检测按钮点击
- ios - 检查哪些字典键对应的数组包含某个字符串
- css - MaterializeCSS 自动完成编辑 css
- python - 通过 Python 将记录插入 Teradata 表的问题
- tags - 通过标签运行 Nightwatch 测试,运行套件中的所有测试
- javascript - VueJs - 承诺返回但重定向不会发生
- assembly - RISC访问地址大于最大整数寄存器
- string - Scala:提取特定字符之前的单词
- ruby-on-rails - 无法从 camt_parser rails gem 获取 iban