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 还可以这样用

2 回复

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 算了

回到顶部