从文本块动态生成生成器函数
(这是我之前发的一个问题的简化版!)
假设你有一个包含有效Python代码的字符串,这段代码里有一个“yield”语句。你想知道怎么才能构建一个生成器,让它执行这个字符串里的代码?
比如,给定这个字符串:
code_string = """for x in range(0, 10):
yield x
"""
我想构建一个生成器f,它能执行code_string,这样在这个特定的例子中:
assert(list(f()) == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
需要注意的是,code_string是任意的,所以这个说法只适用于上面的例子。code_string可以包含任何有效的Python代码,只要里面有yield语句。
谢谢!
编辑:
我想到的第一个解决办法是把“def f():”加到字符串里,然后程序matically给每一行加缩进。但是,如果code_string使用了不同的缩进,这个方法就不行了。我希望能找到一些不太为人知的functools技巧,能从一段文本构建一个函数。
编辑2:
我还尝试在一个函数里用exec,像这样:
code = "for x in range(0, 10): yield x"
def f():
exec code in globals(), locals()
结果是“SyntaxError: 'yield' outside function”(语法错误:'yield'在函数外部)
解决了:我意识到,缩进是相对的,所以这个方法可以:
code_string = """for x in range(0, 10):
yield x
"""
exec "def f():\n" + [(" " + line) for line in code_string.split('\n')]) + "\n"
assert list(f()) == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
2 个回答
试试这个:
exec( """def f():
for x in range(0, 10):
yield x
""" )
for x in f():
print x
你为什么在乎函数里面用什么样的缩进呢?缩进是相对的,不需要保持一致。你只要在前面加上"def f():\n",然后在函数的每一行前面加一个空格,执行一下就可以了。这和你的答案效果是一样的:
exec "def f():\n " + " \n".join(code_string.splitlines()) + "\n"
(听说你的pyparsing解决方案太复杂了,真让人遗憾。听起来你是想要重建一个完整的Python语法,只为了给语言添加一两个功能。在这种情况下,不如不解析整个内容,而是直接添加一些特定的语法,定义一个专门针对这种语法的pyparsing表达式,写一个解析动作来从中生成Python代码,然后用transformString来查找并替换这些特定语法为相应的Python行为。这样你就可以执行或编译整个转换后的模块,只需要一点点pyparsing的帮助。)