原则上,您可以直接修改函数的抽象语法树("abstract syntax tree"),但在实践中可能会变得非常棘手。无论如何,以下是您简单示例的操作方法:
这将从源代码创建一个ast,并从
NodeTransformer
类派生以就地修改ast。节点转换器具有通用的访问方法,遍历节点及其子树并委托给派生类中的特定节点访问者。在这里,我们将所有名称
np
更改为
sp
,然后再更改那些拼写不同的属性为前
np
现在
sp
。您需要将所有这样的差异添加到
translate
字典中。
最后,我们从ast编译回代码对象并执行它,以使修改后的函数可用。
import ast, inspect
import numpy as np
import sympy as sp
def f(a):
return np.array([np.sin(a), np.cos(a)])
z = ast.parse(inspect.getsource(f))
translate = {'array': 'Array'}
class np_to_sp(ast.NodeTransformer):
def visit_Name(self, node):
if node.id=='np':
node = ast.copy_location(ast.Name(id='sp', ctx=node.ctx), node)
return node
def visit_Attribute(self, node):
self.generic_visit(node)
if node.value.id=='sp' and node.attr in translate:
fields = {k: getattr(node, k) for k in node._fields}
fields['attr'] = translate[node.attr]
node = ast.copy_location(ast.Attribute(**fields), node)
return node
np_to_sp().visit(z)
exec(compile(z, '', 'exec'))
x = sp.Symbol('x')
print(f(x))
输出:
[sin(x), cos(x)]
简单增强更新:修改被函数调用的函数:
import ast, inspect
import numpy as np
import sympy as sp
def f(a):
return np.array([np.sin(a), np.cos(a)])
def f2(a):
return np.array([1, np.sin(a)])
def f3(a):
return f(a) + f2(a)
translate = {'array': 'Array'}
class np_to_sp(ast.NodeTransformer):
def visit_Name(self, node):
if node.id=='np':
node = ast.copy_location(ast.Name(id='sp', ctx=node.ctx), node)
return node
def visit_Attribute(self, node):
self.generic_visit(node)
if node.value.id=='sp' and node.attr in translate:
fields = {k: getattr(node, k) for k in node._fields}
fields['attr'] = translate[node.attr]
node = ast.copy_location(ast.Attribute(**fields), node)
return node
from types import FunctionType
for fn in f3.__code__.co_names:
fo = globals()[fn]
if not isinstance(fo, FunctionType):
continue
z = ast.parse(inspect.getsource(fo))
np_to_sp().visit(z)
exec(compile(z, '', 'exec'))
x = sp.Symbol('x')
print(f3(x))
输出:
[sin(x) + 1, sin(x) + cos(x)]
sympy
能够从自己的表达式中生成numpy
表达式。但是sympy
已经在使用符号了。而你的np_fun
是一个 Python 函数,即使它调用了numpy
函数。在.py
文件中,它是一个字符串,但在运行会话中,它已经被解释器解析了。你只能通过inspect
工具获取字符串版本。 - hpauljnp.fun(x)
调用np.sin(x)
,这将变成np.sin(np.array(x))
。np.array(x)
是一个 0d 数组,其中包含一个对象元素,即Symbol
。面对这种类型的数组,np.sin()
尝试执行x.sin()
。这就是你的属性错误的来源。sympy
符号可以在一些numpy
数学运算中使用,但不包括像np.sin
这样的函数。 - hpaulj