Python 3 的函数 Annotations 还可以这样用
Python 3.5 开始支持 Annotations ,可以用来给函数参数加上注解,也有一个叫 mypy 的库用它来做静态类型检查。
前几天发布 Validr 0.14.0 版,在 reddit 上也有人问支不支持 mypy ,今天突发奇想写了个用来运行时检查参数和返回值。
一个简单的实现:
# python >= 3.5
# pip install validr
from validr import SchemaParser, mark_key
def validation(f):
“”“Decorator for validate function params and return by annotations”""
parser = SchemaParser()
return_ = None
params = {}
for k, v in f.annotations.items():
if k == ‘return’:
return_ = parser.parse(v)
else:
params[k] = parser.parse(v)
def wrapper(**kwargs):
for k, v in params.items():
with mark_key(k):
kwargs[k] = v(kwargs.get(k, None))
result = f(**kwargs)
if return_:
with mark_key('return'):
result = return_(result)
return result
return wrapper
@validation
def login(
username: ‘str’,
password: ‘str&minlen=6’,
) -> {
‘id?int’: ‘User ID’,
}:
return {‘id’: 1000000}
if name == ‘main’:
print(login(username=‘guyskk’, password=‘123456’))
print(login(username=‘guyskk’, password=‘1234’))
另外这种写法也是能实现的:
[@validation](/user/validation)
def login(
username: StringType(),
password: StringType(minlen=6),
) -> {
'id': IntType(desc='User ID'),
}:
return {'id': 1000000}
大家觉得这两个写法怎么样,有需要这样一个库的吗
Python 3 的函数 Annotations 还可以这样用
Python 3的函数注解(Annotations)确实不止是类型提示。除了用typing模块做静态类型检查,它还能在运行时通过__annotations__属性获取,实现动态功能。
比如,你可以用注解来做参数验证:
def validate_range(func):
def wrapper(*args, **kwargs):
# 获取函数签名和注解
import inspect
sig = inspect.signature(func)
bound_args = sig.bind(*args, **kwargs)
# 检查每个参数是否符合注解范围
for param_name, value in bound_args.arguments.items():
annotation = sig.parameters[param_name].annotation
if isinstance(annotation, tuple) and len(annotation) == 2:
min_val, max_val = annotation
if not (min_val <= value <= max_val):
raise ValueError(f"{param_name} must be between {min_val} and {max_val}")
return func(*args, **kwargs)
return wrapper
@validate_range
def calculate(x: (0, 100), y: (0, 50)) -> float:
return x * y / 100
# 测试
print(calculate(50, 25)) # 正常
print(calculate(150, 25)) # 抛出 ValueError
还可以用注解实现简单的依赖注入:
class Container:
def __init__(self):
self.services = {}
def register(self, name, service):
self.services[name] = service
def inject(self, func):
def wrapper(*args, **kwargs):
# 根据注解注入依赖
import inspect
sig = inspect.signature(func)
for param_name, param in sig.parameters.items():
if param.annotation in self.services:
kwargs[param_name] = self.services[param.annotation]
return func(*args, **kwargs)
return wrapper
# 使用示例
container = Container()
container.register(str, "Hello")
container.register(int, 42)
@container.inject
def greet(message: str, count: int):
return f"{message} repeated {count} times"
print(greet()) # 输出: Hello repeated 42 times
注解还能用来做文档生成、配置解析等。关键是__annotations__属性在运行时可用,让你能玩出很多花样。
一句话建议:注解不只是类型提示,更是运行时元数据。
打算放弃这种写法 -> 还不如直接写 go 算了

