我的目标是动态生成函数,然后将它们保存在文件中。例如,在我当前的尝试中,在呼叫create_file
import io
def create_file(a_value):
a_func = make_concrete_func(a_value)
write_to_file([a_func], '/tmp/code.py')
def make_concrete_func(a_value):
def concrete_func(b, k):
return b + k + a_value
return concrete_func
def write_to_file(code_list, path):
import inspect
code_str_list = [inspect.getsource(c) for c in code_list]
with open(path, 'w') as ofh:
for c in code_str_list:
fh = io.StringIO(c)
ofh.writelines(fh.readlines())
ofh.write('\n')
create_file('my_value')
我想要的输出是(文件/tmp/code.py
):
def concrete_func(b, k):
return b + k + 'my_value'
我得到的输出是(文件'/tmp/code.py'
):
def concrete_func(b, k):
return b + k + a_value
更新:我的解决方案使用inspect.getsource
返回一个字符串。我想知道我是否限制了你的选择,因为大多数解决方案建议更换字符串。解决方案无需使用inspect.getsource
。无论如何你都可以写它来获得所需的输出。
更新2:我这样做的原因是因为我想为Amazon Lambda生成一个文件。 Amazon Lambda将获取一个python文件及其虚拟环境,并将为您执行它(使您免于担心可伸缩性和容错性)。你必须告诉Lambda要调用哪个文件和哪个函数,Lambda将为你执行它。
答案 0 :(得分:3)
函数定义在定义时不会查找其自由变量(函数本身未定义的变量)。即concrete_func
此处:
def make_concrete_func(a_value):
def concrete_func(b, k):
return b + k + a_value
return concrete_func
在定义时不查找a_value
,而是包含在运行时从其闭包(简化封闭函数)加载a_value
的代码。
您可以通过反汇编返回的函数来看到这一点:
f = make_concrete_func(42)
import dis
print dis.dis(f)
3 0 LOAD_FAST 0 (b)
3 LOAD_FAST 1 (k)
6 BINARY_ADD
7 LOAD_DEREF 0 (a_value)
10 BINARY_ADD
11 RETURN_VALUE
None
你可以通过编辑字节代码来或做你想做的事情..它是在http://bytecodehacks.sourceforge.net/bch-docs/bch/module-bytecodehacks.macro.html .. shudder)之前完成的。
答案 1 :(得分:3)
使用getsource
将函数转换为字符串,并使用简单的字符串操作替换变量名称。
from inspect import getsource
def write_func(fn, path, **kwargs):
fn_as_string = getsource(fn)
for var in kwargs:
fn_as_string = fn_as_string.replace(var, kwargs[var])
with open(path, 'a') as fp: # append to file
fp.write('\n' + fn_as_string)
def base_func(b, k):
return b + k + VALUE
# add quotes to string literals
write_func(base_func, '/tmp/code.py', VALUE="'my value'")
# you should replace the function name if you write multiple functions to the file
write_func(base_func, '/tmp/code.py', base_func='another_func', VALUE='5')
/tmp/code.py中的输出正如预期的那样:
def base_func(b, k):
return b + k + 'my value'
def another_func(b, k):
return b + k + 5
答案 2 :(得分:1)
试试这个。请注意,我已向write_to_file添加了另一个参数
def write_to_file(code_list, path,a_value):
print "lc",code_list
code_str_list = [inspect.getsource(c) for c in code_list]
with open(path, 'w') as ofh:
for c in code_str_list:
c= c.replace('a_value','\''+a_value+'\'')
fh = io.StringIO(c)
ofh.writelines(fh.readlines())
ofh.write('\n')
答案 3 :(得分:1)
如果文件不必是人类可读的并且您相信它不会被攻击者操纵,那么组合functools.partial
和pickle
可能是最pythonic的方法。然而,它有一些我不完全理解的缺点:一方面,它似乎不适用于本地定义的函数(或者通常可能是本地定义的变量?)。
我可能只是问我自己的问题。
import functools
import pickle
def write_file_not_working():
def concrete_func_not_working(b, k, a_value):
return b + k + a_value
with open('data.pickle', 'wb') as f:
data = functools.partial(concrete_func_not_working, a_value='my_value')
pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)
def use_file_not_working():
with open('data.pickle', 'rb') as f:
resurrected_data = pickle.load(f)
print(resurrected_data('hi', 'there'))
def top_level_concrete_func(b, k, a_value):
return a_value + b + k
def write_file_working():
with open('working.pickle', 'wb') as f:
data = functools.partial(top_level_concrete_func, a_value='my_working_value')
pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)
def use_file_working():
with open('working.pickle', 'rb') as f:
resurrected_data = pickle.load(f)
print(resurrected_data('hi', 'there'))
if __name__ == "__main__":
write_file_working()
use_file_working()
write_file_not_working()
use_file_not_working()
答案 4 :(得分:1)
pickle
代替了有限的dill
库,它克服了Ben提到的限制
所以,我终于做了类似的事情。
import dill
def create_file(a_value, path):
a_func = make_concrete_func(a_value)
dill.dump(a_func, open(path, "wb"))
return path
def make_concrete_func(a_value):
def concrete_func(b, k):
return b + k + a_value
return concrete_func
if __name__ == '__main__':
path = '/tmp/code.dill'
create_file('Ben', path)
a_func = dill.load(open(path, "rb"))
print(a_func('Thank ', 'You '))
答案 5 :(得分:0)
如果你想要创建的函数都有一个确定的模式,我会为它创建一个模板并用它来批量生成函数
>>> def test(*values):
template="""
def {name}(b,k):
return b + k + {value}
"""
for i,v in enumerate(values):
print( template.format(name="func{}".format(i),value=repr(v)) )
>>> test("my_value",42,[1])
def func0(b,k):
return b + k + 'my_value'
def func1(b,k):
return b + k + 42
def func2(b,k):
return b + k + [1]
>>>