Python将字符串转为可执行函数,并获取函数的入参、入参默认值、入参类型

在工程开发中有这么一个需求,要求用户可以在前端编写python函数,要求实现以下功能
1、使用Python后端可以执行
2、需要校验用户的入参格式是否正确
3、用户编写函数的默认值在前端展示的功能

基础知识:
在Python中,一个函数对象的__annotations__和__defaults__属性分别代表该函数的入参类型和入参默认值。使用__code__.co_varnames可以获取到函数中的变量(返回一个元祖),结合__code__.co_argcount可以获取参数个数,通过下标就可以拿到入参。

代码如下:

# 这是一个demo函数
def demo(domain, url, method, params, header: Optional[Dict[str, Any]] = {}):
	a=1

# 输出:('domain', 'url', 'method', 'params', 'header', 'a')
print(demo.__code__.co_varnames)
# 输出:5
print(demo.__code__.co_argcount)
# 输出:{'header': typing.Optional[typing.Dict[str, typing.Any]]}
print(demo.__annotations__)
# 输出:({},)
print(demo.__defaults__)

当我们的函数变成一个字符串时,将其编译并创建一个函数对象时,依然可以通过这些属性拿到函数的入参、入参默认值、入参类型。

实现代码:

# 将字符串转函数
def str2funcion(func_str):
    """
    将字符串编译成可执行函数
    :param func_str:
    :return:
    """
    # 编译字符串(compile编译出来的function的作用域是module)
    function = compile(func_str, '', 'exec')
    # 创建一个空命名空间
    globals_dict = {}
    # 在该命名空间中执行该函数
    exec(function, globals_dict)

    # 获取可执行函数(获取module下的code对象,才是我们通过字符串转换出来的可执行函数)
    for const in function.co_consts:
        if isinstance(const, CodeType):
            function = const

    # 通过函数名获取到函数原本的annotations(参数类型)和defaults(参数默认值)
    original_function = globals_dict[function.co_name]
    annotations = original_function.__annotations__
    defaults = original_function.__defaults__

    # 将code对象封装成FunctionType对象,才可以被执行和调用
    function = FunctionType(function, globals())
    # 赋值之前的annotations和defaults, 解决FunctionType转换成可执行函数导致annotations和defaults丢失
    function.__annotations__ = annotations
    function.__defaults__ = defaults

    return function

def get_annotation_by_function(function) -> dict:
	"""
    获取函数入参和入参类型
    :param function:
    :return:
    """
    if isinstance(function, str):
        # 将字符串编译成可执行函数(compile编译出来的function的作用域是module)
        function = compile(function, '', 'exec')
        # 创建一个空命名空间
        globals_dict = {}
        # 在该命名空间中执行该函数
        exec(function, globals_dict)

        # 获取可执行函数(获取module下的code对象,才是我们通过字符串转换出来的可执行函数)
        for const in function.co_consts:
            if isinstance(const, CodeType):
                function = const

        # 通过函数名获取到函数原本的annotations(参数类型)和defaults(参数默认值)
        original_function = globals_dict[function.co_name]
        annotations = original_function.__annotations__
        defaults = original_function.__defaults__

        # 将code对象封装成FunctionType对象,才可以被执行和调用
        function = FunctionType(function, globals())
        # 赋值之前的annotations和defaults, 解决FunctionType转换成可执行函数导致annotations和defaults丢失
        function.__annotations__ = annotations
        function.__defaults__ = defaults

    params = {}
    # 获取函数入参
    parameters = inspect.signature(function).parameters
    for param_name, parameter in parameters.items():
        param_type = parameter.annotation
        params[param_name] = param_type

    return params

def get_params_by_function(function) -> dict:
	"""
    获取函数入参和默认值
    :param function:
    :return:
    """
    if isinstance(function, str):
        # 将字符串编译成可执行函数(compile编译出来的function的作用域是module)
        function = compile(function, '', 'exec')
        # 创建一个空命名空间
        globals_dict = {}
        # 在该命名空间中执行该函数
        exec(function, globals_dict)

        # 获取可执行函数(获取module下的code对象,才是我们通过字符串转换出来的可执行函数)
        for const in function.co_consts:
            if isinstance(const, CodeType):
                function = const

        # 通过函数名获取到函数原本的annotations(参数类型)和defaults(参数默认值)
        original_function = globals_dict[function.co_name]
        annotations = original_function.__annotations__
        defaults = original_function.__defaults__

        # 将code对象封装成FunctionType对象,才可以被执行和调用
        function = FunctionType(function, globals())
        # 赋值之前的annotations和defaults, 解决FunctionType转换成可执行函数导致annotations和defaults丢失
        function.__annotations__ = annotations
        function.__defaults__ = defaults
        print(function.__code__.co_varnames)

    params = {}
    # 获取函数入参
    parameters = inspect.signature(function).parameters
    for param_name, parameter in parameters.items():
        param_type = parameter.annotation
        param_default = parameter.default
        if param_default is inspect._empty:
            params[param_name] = None
        else:
            params[param_name] = param_default

    return params

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值