开发者

Dynamically generating a generator function from a blob of text

开发者 https://www.devze.com 2023-01-04 08:07 出处:网络
(Simplified from an excessively verbose question I posted earlier!) Given a Python string containing valid Python code that contains a \"yield\" statement, how can I construct a generator that exec\'

(Simplified from an excessively verbose question I posted earlier!)

Given a Python string containing valid Python code that contains a "yield" statement, how can I construct a generator that exec's that string?

For example, given the string:

code_string = """for x in range(0, 10):
    yield x
"""

I want to construct a generator f that executes code_string such that (in this particular example):

assert(list(f()) == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

Note that code_string is arbitrary, so this assertion is only valid for the example above. code_string can contain any valid python code that contains a yield statement.

Thanks!

Edit:

The first solution I thought of was to just munge "def f():" into the string and indent each line programmatically. However, that fails if code_string uses different indentation. I was hoping there were some little-known functools kung-fu that can construct a function from a blob of text.

Edit2:

I also tried an exec inside a function like so:

code = 开发者_StackOverflow"for x in range(0, 10): yield x"
def f():
    exec code in globals(), locals()

This results in "SyntaxError: 'yield' outside function"

Solved: I stand corrected, indentation is relative, so this works:

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]


What do you care what indentation is used within the function? Indentation is relative and does not have to be consistent. Tack "def f():\n" on the front, add a single space to each line in your function and exec it, and you're done. This works just as well as your answer:

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

(Sorry to hear your pyparsing solution was too complicated. It sounds like you were trying to recreate a full Python grammar just to add a feature or two to the language. In this case, instead of parsing the whole thing using parseString, you can just add some specialized syntax, define a pyparsing expression for just that syntax, write a parse action that creates Python code from it, and use transformString to search and replace the special syntax with the corresponding Python behavior. Then you can exec or compile the whole transformed module, with just a little bit of pyparsing.)


Try:

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


for x in f():
    print x
0

精彩评论

暂无评论...
验证码 换一张
取 消