如何将一行代码注入现有函数?
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
【讨论】:
以上是关于如何将一行代码注入现有函数?的主要内容,如果未能解决你的问题,请参考以下文章