Executar código Python contida em uma corda
Pergunta
Eu estou escrevendo um motor de jogo usando pygame e box2d, e no construtor personagem, eu quero ser capaz de escrever o código que será executado em eventos keydown.
Meu plano era ter um editor de texto no construtor de caráter que lhe permitem escrever código semelhante a:
if key == K_a:
## Move left
pass
elif key == K_d:
## Move right
pass
Vou recuperar o conteúdo do editor de texto como uma string, e eu quero o código para ser executado em um método neste método de caracteres:
def keydown(self, key):
## Run code from text editor
Qual é a melhor maneira de fazer isso?
Solução
Você pode usar o método eval(string)
para fazer isso.
Definição
eval(code, globals=None, locals=None)
O código é um código Python apenas padrão - isso significa que ele ainda precisa ser devidamente recuadas.
Os globals pode ter um __builtins__
personalizado definido, que poderia ser útil para fins de segurança.
Exemplo
eval("print('Hello')")
Será que hello
de impressão para o console. Você também pode especificar variáveis ??locais e globais para o código para uso:
eval("print('Hello, %s'%name)", {}, {'name':'person-b'})
preocupações de segurança
Tenha cuidado, no entanto. Qualquer entrada do usuário será executado. Considere o seguinte:
eval("import os;os.system('sudo rm -rf /')")
Há uma série de maneiras de contornar isso. O mais fácil é fazer algo como:
eval("import os;...", {'os':None})
O que irá lançar uma exceção, em vez de apagar seu disco rígido. Enquanto o seu programa é desktop, este poderia ser um problema se as pessoas redistribuído scripts, que eu imagino se destina.
estranho Exemplo
Aqui está um exemplo do uso eval
em vez estranhamente:
def hello() : print('Hello')
def world() : print('world')
CURRENT_MOOD = 'happy'
eval(get_code(), {'contrivedExample':__main__}, {'hi':hello}.update(locals()))
O que isto significa na linha eval é:
- Dá o módulo atual outro nome (torna-se
contrivedExample
para o script). O consumidor pode chamarcontrivedExample.hello()
agora.) - Define
hi
como apontando parahello
- Combinou que dicionário com a lista de globals atuais no módulo de execução.
FALHA
Acontece (comentadores obrigado!) Que você realmente precisa usar a declaração exec
. Big oops. Os exemplos revistos são os seguintes:
exec
Definição
(Esta aparência! Familiarizado)
Exec é uma declaração:
exec "code" [in scope]
Onde escopo é um dicionário de ambas as variáveis ??locais e globais. Se isso não for especificado, ele é executado no escopo atual.
O código é o código Python apenas padrão - isso significa que ele ainda precisa ser devidamente recuadas.
exec
Exemplo
exec "print('hello')"
Será que hello
de impressão para o console. Você também pode especificar variáveis ??locais e globais para o código para uso:
eval "print('hello, '+name)" in {'name':'person-b'}
exec
Preocupações com a segurança
Tenha cuidado, no entanto. Qualquer entrada do usuário será executado. Considere o seguinte:
exec "import os;os.system('sudo rm -rf /')"
Declaração de impressão
Como também observado por comentaristas, print
é uma declaração em todas as versões do Python antes de 3.0. No 2.6, o comportamento pode ser alterado por from __future__ import print_statement
digitação. Caso contrário, use:
print "hello"
Em vez de:
print("hello")
Outras dicas
Como outros apontaram, você pode carregar o texto em uma seqüência e uso exec "codestring"
. Se contido em um arquivo já, usando execfile vai evitar ter que carregá-lo.
Uma nota de desempenho: Você deve evitar execing as múltiplas código vezes, como analisar e compilar a fonte python é um processo lento. ie. não tem:
def keydown(self, key):
exec user_code
Você pode melhorar esta um pouco a compilar a fonte em um objeto de código (com compile()
e exec que, ou melhor, através da construção de uma função que você mantenha ao redor, e só construir uma vez. Ou exigir que o usuário escrever "def my_handler (args ...)", ou preceder-lo sozinho, e fazer algo como:
user_source = "def user_func(args):\n" + '\n'.join(" "+line for line in user_source.splitlines())
d={}
exec user_source in d
user_func = d['user_func']
Então, mais tarde:
if key == K_a:
user_func(args)
Você pode usar eval()
eval ou exec. Você deve definitivamente ler referência da biblioteca Python antes da programação.