我正在尝试应用装饰器来更改功能代码,然后使用更改后的代码执行此功能。
下面是具有示例功能的temp
模块。如果将[*args, **kwargs, 123]
应用于此函数,我只是希望该函数返回[*args, **kwargs]
而不是some_decorator
。
from dill.source import getsource
def some_decorator(method):
def wrapper(*args, **kwargs):
source_code = getsource(method)
code_starts_at = source_code.find('):') + 2
head = source_code[:code_starts_at]
body = source_code[code_starts_at:]
lines = body.split('\n')
return_line = [i for i in lines if 'return' in i][0]
old_expr = return_line.replace(' return ', '')
new_expr = old_expr.replace(']', ', 123]')
new_expr = head + '\n' + ' return ' + new_expr
new_expr = new_expr + '\n' + method.__name__ + '(*args, **kwargs)'
return eval(new_expr)
return wrapper
@some_decorator
def example_func(*args, **kwargs):
return [*args, **kwargs]
更多说明:我正在重写原始功能
def example_func(*args, **kwargs):
return [*args, **kwargs]
to
def example_func(*args, **kwargs):
return [*args, **kwargs, 123]
example_func(*args, **kwargs)
希望eval
能够编译并运行此修改的函数。
当我尝试按照下面的代码运行它时,它返回语法错误。
from temp import example_func
example_func(5)
我知道eval
可以解决这个问题:
[*args, **kwargs, 123]
但仅当已经声明args
和kwargs
时。我希望在执行example_func(args, kwargs)
时从example_func
中读取它们。
我想只是将修改后的功能代码写入文件中
def example_func(*args, **kwargs):
return [*args, **kwargs, 123]
并使some_decorator
执行修改后的代码而不是原始代码的功能,就可以了。但是,理想情况下,我将跳过创建任何中间文件的过程。
是否可以实现?
虽然从技术上讲,您几乎可以使用Python中的函数和装饰器来做任何事情,但您[[不应。
在这种情况下,向返回列表的函数添加额外的值很简单:def some_decorator(method):
def wrapper(*args, **kwargs):
result = method(*args, **kwargs)
return result + [123]
return wrapper
这不需要任何功能代码重写。如果您要做的只是更改函数的输入或输出,请,并保留函数本身。请注意,只需更改输入或输出
eval()
不能
会更改您的功能,因为eval()
严格限于表达式。创建函数的def
语法为statement。从根本上讲,语句可以包含表达式和其他语句(例如if <test_expression>: <body of statements>
),但表达式不能包含语句。要将文本作为任意Python代码执行,您必须改用exec()
function。接下来,用于重建Python代码的文本处理非常低效且容易出错。您最好让Python将源代码编译为抽象语法树
(通过exec()
),然后在该树上工作。定义明确的对象的有向图比文本容易操作(在使用多少空白等方面更灵活)。有关重写Python代码的项目的真实示例,请查看ast
module。他们使用模块导入钩子来执行此操作,但是只要功能可使用源代码,您也可以使用装饰器来执行此操作。使用ast
模块来更改how pytest rewrites the assert
statement to add extra context语句中的列表:
assert
请注意,此。您无需在每次尝试调用函数时都进行重新处理,装饰器只需执行一次即可返回该结果。不需要包装函数