从文本块动态生成生成器函数

1 投票
2 回答
1522 浏览
提问于 2025-04-16 00:21

(这是我之前发的一个问题的简化版!)

假设你有一个包含有效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 个回答

1

试试这个:

exec( """def f():
        for x in range(0, 10):
               yield x
""" )


for x in f():
    print x
2

你为什么在乎函数里面用什么样的缩进呢?缩进是相对的,不需要保持一致。你只要在前面加上"def f():\n",然后在函数的每一行前面加一个空格,执行一下就可以了。这和你的答案效果是一样的:

exec "def f():\n " + " \n".join(code_string.splitlines()) + "\n" 

(听说你的pyparsing解决方案太复杂了,真让人遗憾。听起来你是想要重建一个完整的Python语法,只为了给语言添加一两个功能。在这种情况下,不如不解析整个内容,而是直接添加一些特定的语法,定义一个专门针对这种语法的pyparsing表达式,写一个解析动作来从中生成Python代码,然后用transformString来查找并替换这些特定语法为相应的Python行为。这样你就可以执行或编译整个转换后的模块,只需要一点点pyparsing的帮助。)

撰写回答