首页 > 解决方案 > Python 运行时类型检查传递给装饰器的函数是否具有正确的签名

问题描述

我有一个 python 装饰器,它期望传递函数的签名是 aUser和 a Point,它返回一个具有相同签名的函数:

@dataclass
class User:
    name: str

@dataclass
class Point:
    x: float
    y: float
    z: float


def logging_decorator(f: Callable[[User, Point], bool]) -> Callable[[User, Point], bool]:
    print('decorating function...')
    def decorated(a: User, b: Point) -> bool:
        print(f'User {a.name} calling with points {b.x},{b.y},{b.z}')
        ret = f(a, b)
        print(f'returned {ret}')
        return ret
    return decorated

在打印decorating function...时,我希望它检查传入的函数是否具有正确的签名,如果没有,则给出合理的错误消息。

这是一个装饰两个函数的示例,一个具有正确的签名,一个具有不正确的签名:

@logging_decorator
def good_f(a: User, b: Point) -> bool:
    return True

@logging_decorator
def bad_f(a: str, b: str) -> str:
    return "Hello"

如何向 中添加检查,logging_decorator以便检查被修饰的函数是否具有正确的参数类型?

我尝试使用typing_inspectandtypeguard包,但找不到我需要的功能。

我正在使用 Python 3.8.3

标签: pythonpython-decoratorspython-typing

解决方案


使用inspect标准库中的模块。使用inspect.signature,您可以编写如下函数验证代码:

import inspect

def validate_func(func):
    def good_func(a: User, b: Point) -> bool: ...
    good_func_signature = inspect.signature(good_func)
    received_signature = inspect.signature(func)
    if received_signature != good_func_signature:
        raise TypeError(
            f'Invalid function signature: '
            f'expected "{good_func_signature}", '
            f'got "{received_signature}"'
        )

(您可以将此逻辑放在您的用例的装饰器中;我只是将其编写为一个单独的函数,以使其与您的代码分开。)

使用中:

>>> validate_func(good_f)
>>> validate_func(lambda a, b: a)
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "<string>", line 6, in validate_func
TypeError: Invalid function signature: expected "(a:__main__.User, b:__main__.Point) -> bool", got "(a, b)"
>>> validate_func(bad_f)
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "<string>", line 6, in validate_func
TypeError: Invalid function signature: expected "(a:__main__.User, b:__main__.Point) -> bool", got "(a:str, b:str) -> str"

如果您愿意,您可以在签名之间进行更详细和细粒度的比较,而不仅仅是简单的相等检查。您可以使用 from 的返回值inspect.signature进行比较:

  • 参数个数
  • 参数名称
  • 参数的种类(仅位置、位置或关键字或仅关键字)
  • 参数是否有注解
  • 参数的注解是什么
  • 参数是否有默认值
  • 参数的默认值是什么
  • 函数是否有返回注解
  • 函数的返回注解是什么

inspect.signature 您可以在此处找到文档。


推荐阅读