如何将一行代码注入到现有函数中?
How to inject a line of code into an existing function?
我的函数如下所示:
async def callback(ctx, first_arg, second_arg=None)
...
其中参数名称、计数和所有内容始终可以不同。
在第一个参数中是一个名为 respond()
的方法,我想制作一个装饰器 returns 修改后的回调,其中 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直接传过去,结果检查不行
假设我有这个例子
@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
方法,我将“绕过”参数检查
请注意,需要进行参数检查,因为我需要在装饰器中获取有关它们的一些信息
所以现在有两个选择,第一个是像我说的那样注入一行代码或以某种方式“克隆”回调函数的参数并设置它们到“假”回调函数
顺便说一句,抱歉英语不好,如果我没有表达正确或者在这个问题中缺少某些信息,请告诉我,以便我改进问题!
我想你想要这样的东西(为了简单起见,我删除了异步的东西,你可以随意添加回来):
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
和 **kwargs
就像第一个装饰器不存在一样感谢 @functools.wraps
我的函数如下所示:
async def callback(ctx, first_arg, second_arg=None)
...
其中参数名称、计数和所有内容始终可以不同。
在第一个参数中是一个名为 respond()
的方法,我想制作一个装饰器 returns 修改后的回调,其中 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直接传过去,结果检查不行
假设我有这个例子
@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
方法,我将“绕过”参数检查
请注意,需要进行参数检查,因为我需要在装饰器中获取有关它们的一些信息
所以现在有两个选择,第一个是像我说的那样注入一行代码或以某种方式“克隆”回调函数的参数并设置它们到“假”回调函数
顺便说一句,抱歉英语不好,如果我没有表达正确或者在这个问题中缺少某些信息,请告诉我,以便我改进问题!
我想你想要这样的东西(为了简单起见,我删除了异步的东西,你可以随意添加回来):
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
和 **kwargs
就像第一个装饰器不存在一样感谢 @functools.wraps