gpt4 book ai didi

python - 在装饰器中修改函数

转载 作者:太空狗 更新时间:2023-10-29 17:34:10 24 4
gpt4 key购买 nike

我正在考虑制作装饰器以提高性能。一个装饰器,修改它装饰的函数的源代码,并返回修改后的函数。

经过深思熟虑,我想如果我能拿到函数的源代码,我就可以做到这一点。但是是否可以在装饰器中访问函数的源代码?如果我有这样的装饰器:

import inspect

def decorate(f):
exec(inspect.getsource(f))
return eval(f.__name__)

@decorate
def test():
return 1

我得到一个操作系统错误:

OSError: could not get source code

这似乎是因为 test 在被传递到 decorate 之前没有完全形成。但是,这有效:

import inspect

def decorate(f):
exec(inspect.getsource(f))
return eval(f.__name__)

def test():
return 1
test = decorate(test)

不过,它只是没有那种装饰风格。这似乎是可能的,因为 f.__code__ 已定义


经过进一步检查,似乎只有当我将 inspect.getsource(f) 放入 exec 时才会发生这种情况。否则,我似乎可以得到源代码。


我首先想到的是尾递归。不幸的是,我写了这个装饰器,它很慢并且需要一种非常特殊的编写要装饰的函数的风格:

def tail_recurse(acc_default):
def decorate(f):
def wrapper(*args, acc=acc_default):
args = args + (acc,)
while True:
return_type, *rargs = f(*args)
if return_type is None:
return rargs[-1]
args = rargs
return wrapper
return decorate

基本上,我正在考虑做一些简单的事情,比如将函数体替换为:

while True:
__body__
update_args

最佳答案

您可以使用 functools.wraps使用您的原始代码:

import inspect
from functools import wraps

@wraps
def decorate(f):
exec(inspect.getsource(f))
return eval(f.__name__)

@decorate
def test():
return 1

输出:

In [2]: test()
Out[2]: 1

如果您计划在运行时更改源代码,那么您应该熟悉 ast图书馆,有一个优秀的video from pycon 2011 Matthew Desmarais 就如何使用 ast 模块将源代码从基础更改为更高级的选项进行了演讲,这是演讲中使用的 python 到 javascript 转换器的一个简单工作示例,它将适用于提供的 fib 函数等简单示例。

它应该让您很好地理解 NodeTransformer 的工作原理,这就是您想要在运行时用来操作代码的东西,您可以使用类似于下面的 dec 函数的东西来装饰您的函数,不同之处在于您将返回编译后的代码:

from ast import parse, NodeTransformer


class Transformer(NodeTransformer):
def __init__(self):
self.src = ""
self.indent = 0

def translate(self, node):
self.visit(node)
return self.src

def _indent(self, line):
return "{}{line}".format(" " * self.indent, line=line)

def render(self, body):
self.indent += 2
for stmt in body:
self.visit(stmt)
self.indent -= 2

def visit_Num(self, node):
self.src += "{}".format(node.n)

def visit_Str(self, node):
self.src += "{}".format(node.s)

def visit_FunctionDef(self, defn):
args = ",".join(name.arg for name in defn.args.args)
js_defn = "var {} = function({}){{\n"
self.src += self._indent(js_defn.format(defn.name, args))
self.render(defn.body)
self.src += self._indent("}\n")

def visit_Eq(self, less):
self.src += "=="

def visit_Name(self, name):
self.src += "{}".format(name.id)

def visit_BinOp(self, binop):
self.visit(binop.left)
self.src += " "
self.visit(binop.op)
self.src += " "
self.visit(binop.right)

def visit_If(self, _if):
self.src += self._indent("if (")
self.visit(_if.test)
self.src += ") {\n"
self.render(_if.body)
self.src += " "*self.indent + "}\n"


def visit_Compare(self, comp):
self.visit(comp.left)
self.src += " "
self.visit(comp.ops[0])
self.src += " "
self.visit(comp.comparators[0])

def visit_Call(self, call):
self.src += " "
self.src += "{}(".format(call.func.id)
self.visit(call.args[0])
self.src += ")"

def visit_Add(self, add):
self.src += "+"

def visit_Sub(self, add):
self.src += "-"

def visit_Return(self, ret):
self.src += self._indent("return")
if ret.value:
self.src += " "
self.visit(ret.value)
self.src += ";\n"


def dec(f):
source = getsource(f)
_ast = parse(source)
trans = Transformer()
trans.indent = 0
return trans.translate(_ast)


from inspect import getsource


def fibonacci(n):
if n == 0:
return 0
if n == 1:
return 1
return fibonacci(n - 1) + fibonacci(n - 2)

运行 dec 函数将我们的 python 输出为 javascript:

print(dec(fibonacci))
var fibonacci = function(n){
if (n == 0) {
return 0;
}
if (n == 1) {
return 1;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}

greentreesnakes文档也值得一读。

关于python - 在装饰器中修改函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31078439/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com