运行包含在字符串中的Python代码

9 投票
4 回答
23612 浏览
提问于 2025-04-15 12:21

我正在用pygame和box2d写一个游戏引擎,在角色构建器里,我想能写一些代码,这些代码会在按下键的时候执行。

我的计划是在角色构建器里放一个文本编辑器,让你可以写类似这样的代码:

if key == K_a:
    ## Move left
    pass
elif key == K_d:
    ## Move right
    pass

我会把文本编辑器里的内容提取出来,作为一个字符串,然后我想在这个角色的方法里运行这些代码:

def keydown(self, key):
    ## Run code from text editor

这样做的最佳方法是什么呢?

4 个回答

0

你可以使用 eval() 这个函数。

2

正如其他人提到的,你可以把文本加载到一个字符串里,然后使用 exec "codestring" 来执行它。如果这个代码已经在一个文件里,使用 execfile 就可以避免先加载它。

有一点性能上的建议:你应该避免多次执行同样的代码,因为解析和编译 Python 源代码是一个比较慢的过程。也就是说,不要这样做:

def keydown(self, key):
    exec user_code

你可以通过把源代码编译成一个代码对象(使用 compile()),然后执行这个对象来稍微提高性能,或者更好的是,构建一个函数并保留它,只需构建一次。这样用户需要写 "def my_handler(args...)",或者你自己在前面加上,然后做类似这样的事情:

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']

然后在后面:

if key == K_a:
   user_func(args)
25

你可以使用 eval(string) 方法来实现这个功能。

定义

eval(code, globals=None, locals=None)
这里的代码就是标准的 Python 代码,这意味着它需要正确缩进。

globals 可以定义一个自定义的 __builtins__,这在安全方面可能会很有用。

示例

eval("print('Hello')")

这段代码会在控制台打印 hello。你还可以为代码指定局部和全局变量:

eval("print('Hello, %s'%name)", {}, {'name':'person-b'})

安全问题

不过要小心,任何用户输入都会被执行。想想看:

eval("import os;os.system('sudo rm -rf /')")

有很多方法可以解决这个问题。最简单的做法是:

eval("import os;...", {'os':None})

这样会抛出一个异常,而不是擦除你的硬盘。虽然你的程序是在桌面上运行,但如果有人重新分发脚本,这可能会成为一个问题,我想这也是你不希望的。

奇怪的示例

这里有一个使用 eval 的奇怪示例:

def hello() : print('Hello')
def world() : print('world')
CURRENT_MOOD = 'happy'

eval(get_code(), {'contrivedExample':__main__}, {'hi':hello}.update(locals()))

在 eval 行中,这段代码做了以下几件事:

  1. 给当前模块起了个新名字(在脚本中变成了 contrivedExample)。现在使用者可以调用 contrivedExample.hello()。)
  2. hi 定义为指向 hello
  3. 将这个字典与当前模块中的全局变量列表结合起来。

失败

结果是(感谢评论者!)你实际上需要使用 exec 语句。真是个大失误。修正后的示例如下:


exec 定义

(这听起来很熟悉!) Exec 是一个语句:
exec "code" [in scope] 其中 scope 是一个包含局部和全局变量的字典。如果没有指定,它将在当前作用域中执行。

这里的代码也是标准的 Python 代码,这意味着它仍然需要正确缩进。

exec 示例

exec "print('hello')"

这段代码会在控制台打印 hello。你也可以为代码指定局部和全局变量:

eval "print('hello, '+name)" in {'name':'person-b'}

exec 安全问题

不过要小心,任何用户输入都会被执行。想想看:

exec "import os;os.system('sudo rm -rf /')"

打印语句

正如评论者所提到的,print 在 Python 3.0 之前的所有版本中都是一个语句。在 2.6 中,可以通过输入 from __future__ import print_statement 来改变其行为。否则,请使用:

print "hello"

而不是:

print("hello")

撰写回答