如何将一行代码注入现有函数?

Posted

技术标签:

【中文标题】如何将一行代码注入现有函数?【英文标题】:How to inject a line of code into an existing function? 【发布时间】:2021-11-03 15:19:43 【问题描述】:

我的函数如下所示:

async def callback(ctx, first_arg, second_arg=None)
    ...

参数名称、计数和所有内容总是不同的。 在第一个参数中是一个名为respond() 的方法,我想制作一个返回修改后的回调的装饰器,其中调用了ctx.respond()

我想备份原始回调,然后创建一个“假”回调,调用 ctx.respond() 方法,然后调用真正的回调,但它不起作用,因为在我的装饰器之后还有另一个装饰器将检查函数参数

装饰器

def auto_respond():
    def wraper(callback):
        # this is what I thought of
        b_callback = callback
        async def auto_callback(ctx, here comes the problem):
            await ctx.respond()
            return await b_callback(ctx, the problem is here too)
        return auto_callback
    return wraper

问题是,我无法设置函数的正确参数,因为我现在不知道它们会是什么。

本来想用*args**kwargs来接收params直接传过去,结果check就不行了


假设我有这个例子

@the_other_decorator()
@auto_respond()
async def callback(ctx, user=None):
    ...

在另一个装饰器中,参数将使用inspect.signature(callback).parameters进行检查

the_other_decorator

def the_other_decorator():
    def wraper(callback):
        params = inspect.signature(callback).parameters
        for name in params:
            param = params.get(name)
            if param.annotation != param.empty:
                ...
            elif param.default != inspect._empty:
                ...
        ...
    return wraper

所以我的下一个解决方案是,如果我能以某种方式向回调函数“注入”一行代码,获取第一个参数并使用它的 respond 方法,我将“绕过”参数检查

请注意,需要进行参数检查,因为我需要在装饰器中获取有关它们的一些信息

所以现在剩下两个选项,第一个是像我说的那样注入一行代码以某种方式“克隆”回调函数的参数并将它们设置为“假”回调函数

顺便说一句,抱歉英语不好,如果我表达不正确或此问题中缺少某些信息,请告诉我,以便我改进问题!

【问题讨论】:

旁注:可以跳过wrapper装饰器,def auto_respond(callback): 【参考方案1】:

我认为您想要这样的东西(为简单起见,我删除了异步内容,请随意添加它们):

import functools
import inspect
def auto_respond(func):
    # This will preserve the signature of func
    @functools.wraps(func)
    def wraper(*args, **kwargs):
        ctx = args[0]
        ctx.respond()
        return func(*args, **kwargs)
    return wraper

class Context:
    def respond(self):
        print("Response")

my_context = Context()  
@auto_respond
def my_func_1(ctx, arg1, arg2):
    print(arg1, arg2)
my_func_1(my_context, "a", "b")
print(inspect.signature(my_func_1).parameters)

@auto_respond
def my_func_2(ctx, arg1, arg2, arg3, arg4=None):
    print(arg1, arg2, arg3, arg4)
my_func_2(my_context, "a", "b", "c", "d")
print(inspect.signature(my_func_2).parameters)

>>>>>>
Response
a b
OrderedDict([('ctx', <Parameter "ctx">), ('arg1', <Parameter "arg1">), ('arg2', <Parameter "arg2">)])
Response
a b c d
OrderedDict([('ctx', <Parameter "ctx">), ('arg1', <Parameter "arg1">), ('arg2', <Parameter "arg2">), ('arg3', <Parameter "arg3">), ('arg4', <Parameter "arg4=None">)])

如果你想获取函数并将其存储在某个地方,你也可以这样做:

def my_func_3(ctx, arg1, arg2):
    print(arg1, arg2)
my_auto_respond_func = auto_respond(my_func_3)

my_auto_respond_func(my_context, "a", "b")

要回答您问题的第二部分,您可以使用与 auto_respond 相同的结构在此装饰器之上链接另一个装饰器,并且您将能够检查参数,因为这些参数是通过 *args 和 @ 传递的987654324@ 好像第一个装饰器不存在,感谢@functools.wraps

【讨论】:

以上是关于如何将一行代码注入现有函数?的主要内容,如果未能解决你的问题,请参考以下文章

如何将此代码减少到一行?

java中的文件读取-将每一行转换为现有函数

如何首先使用实体​​框架代码更新一行?

是否可以附加到现有文件的第一行?

如何使用第一行的列名将 CSV 导入 BigQuery 上的现有表?

如何使用 Word VBA 宏在列标题下方添加一行并向下移动现有行?