exec devuelve un no tipo, cuando necesito el valor de retorno

Hice una pregunta antes, pero encontré un segundo problema.

Estoy escribiendo un progtwig que lee en un archivo de texto y ejecuta todo el código dentro del archivo. Esto es para la clase, y tenemos que usar exec()

Recibo este error al ejecutar el código, e innumerables búsquedas no me han llevado a una solución.

 Traceback (most recent call last): File "doxecute.py", line 28, in  replace_code(statements, contents) File "doxecute.py", line 17, in replace_code contents = contents.replace("{%" + statement + "%}", statement) TypeError: Can't convert 'NoneType' object to str implicitly 

El código es

 import sys import re def sortecute(data): funcs = re.findall(r'{%(.*?)%}',data,re.DOTALL)#find executable statements return funcs def replace_code(statements, contents): for statement in statements: if not statement[5:].startswith("print("): exec(statement[5:]) #execute code after the (letter) contents = contents.replace("{%" + statement + "%}", "") else: statement = exec(statement[5:])#error is here contents = contents.replace("{%" + statement + "%}", statement) print(contents) f = open(sys.argv[1],"r") contents = f.read() f.close() statements = sortecute(contents) #get data from file statements = sorted(statements) #sorts by letter replace_code(statements, contents) 

Este es el archivo que leí.

 The number {% (c) print(x) %} is a random number between 1 and 6 inclusive. If we multiply it by 2, we get {% (d) print(2*x) %}. What's interesting is that the statements may appear out of order in the document. {% (a) import random %} Thus I might generate the random number in a location in the document well after referencing it. {% (b) x = random.randint(1,6) %} 

No puedo averiguar cómo obtener el valor de la statement para exec. ¿Puede alguien explicarme cómo usar esto correctamente de la manera que se indica a continuación?

 You will need to use the exec function in Python. To get the output back, you will need to redirect output to your own stream. Your program should accept a filename as a command-line argument to operate on [8] 

exec siempre devolverá None . De la documentación :

exec ( objeto [, globals [, locals]] )

Esta función es compatible con la ejecución dinámica de código Python. objeto debe ser una cadena o un objeto de código. Si es una cadena, la cadena se analiza como un conjunto de sentencias de Python que luego se ejecuta (a menos que se produzca un error de syntax). [1] Si es un objeto de código, simplemente se ejecuta. En todos los casos, se espera que el código que se ejecuta sea válido como entrada de archivo (consulte la sección “Entrada de archivo” en el Manual de referencia). Tenga en cuenta que las declaraciones de return y yield no se pueden usar fuera de las definiciones de funciones, incluso dentro del contexto del código pasado a la función exec() . El valor de retorno es None .

Esta es una petición bastante extraña. Pero puedes capturar la salida de esta manera:

 >>> s = """The number {% (c) print(x) %} is a random number between 1 and 6 ... inclusive. If we multiply it by 2, we get {% (d) print(2*x) %}. ... ... What's interesting is that the statements may appear out of order in the ... document. {% (a) import random %} Thus I might generate the random ... number in a location in the document well after referencing it. ... {% (b) x = random.randint(1,6) %}""" >>> import re >>> stmts = re.findall(r'{%\s*\((\w*)\)\s*(.*)%}',s) >>> stmts [('c', 'print(x) '), ('d', 'print(2*x) '), ('a', 'import random '), ('b', 'x = random.randint(1,6) ')] 

Ahora, tiene que redirigir la salida a una secuencia que puede manipular más adelante:

 >>> import io >>> import sys >>> stream = io.StringIO() >>> stdout = sys.stdout # this keeps stdout so we can set it back >>> sys.stdout = stream >>> for _, statement in sorted(stmts): ... exec(statement) ... >>> sys.stdout = stdout # remember to reset stdout! 

Y ahora, puede obtener los valores que fueron impresos:

 >>> stream.getvalue() '5\n10\n' >>> stream.getvalue().split() ['5', '10'] 

Aunque, creo que una forma más fácil es pasar un espacio de nombres al dict:

 >>> namespace = {} >>> for _, statement in sorted(stmts): ... exec(statement, namespace) ... 5 10 >>> namespace.keys() dict_keys(['__builtins__', 'random', 'x']) 

El espacio de nombres se cargará con los __builtins__ normales a menos que usted proporcione uno. Entonces, para obtener cada nombre creado en su código ejecutado, puede encontrar la diferencia entre la vista de namspace.keys dictview y un conjunto que contine la cadena "__builtins__"

 >>> namespace.keys() dict_keys(['__builtins__', 'random', 'x']) >>> vals = namespace.keys() - {'__builtins__'} >>> vals {'random', 'x'} >>> for val in vals: ... print(namespace[val]) ...  5 >>> 

Aunque, si estás en Python 3.4> = es mucho más fácil redirigir la salida estándar a alguna secuencia:

 >>> import contextlib >>> stream = io.StringIO() >>> with contextlib.redirect_stdout(stream): ... for _, statement in stmts: ... exec(statement) ... >>> stream.getvalue() '5\n10\n' >>> stream.getvalue().split() ['5', '10']