【问题标题】:How do I execute a string containing Python code in Python?如何在 Python 中执行包含 Python 代码的字符串?
【发布时间】:2010-10-16 15:39:58
【问题描述】:

如何在 Python 中执行包含 Python 代码的字符串?

【问题讨论】:

  • 公平地说,与其他问题不同,这个问题不是重复的。还有其他人发布了比这更多的基本问题。
  • 当然,正确答案几乎总是“不要!”。
  • @S. Lott:最近没有阅读常见问题解答吗? “问和回答你自己的编程问题也很好,但假装你在危险中:以问题的形式表达它”。这不是一个坏问题。 +1
  • @S.Lott:你没有。你不需要。如果问题不在网站上,那么这是公平的游戏(根据常见问题解答,正如已经指出的那样)。只需回答每个问题,就好像 OP 需要帮助一样。他们可能不会,但下一个阅读他们问题的人可能会。只是我的 2 美分。
  • 致所有你说不要这样做的人:我有一个案例,我绝对需要这样做。它涉及分布式处理。

标签: python string exec


【解决方案1】:

对于语句,请使用 exec(string) (Python 2/3) 或 exec string (Python 2):

>>> mycode = 'print "hello world"'
>>> exec(mycode)
Hello world

当你需要表达式的值时,使用eval(string):

>>> x = eval("2+2")
>>> x
4

但是,第一步应该是问问自己是否真的需要。执行代码通常应该是不得已的位置:如果它可以包含用户输入的代码,它会变得缓慢、丑陋和危险。您应该始终首先查看替代方案,例如高阶函数,看看它们是否能更好地满足您的需求。

【讨论】:

  • 但是'exec'执行的代码的范围呢?是嵌套的吗?
  • 某人想要使用'exec'的常见情况是if s=='foo': x.foo = 42 elif s=='bar': x.bar = 42等,然后他们可以写成exec ("x.%s = 42" % s)。对于这种常见情况(您只需要访问存储在字符串中的对象的属性),有一个更快、更清晰、更安全的函数getattr:只需写getattr(x, s) = 42 来表示相同的意思。
  • exec 怎么比 python 解释器慢?
  • @ShreevatsaR 你不是说setattr(x, s, 42)吗?我试过getattr(x, 2) = 42,但失败了can't assign to function call: <string>, line 1
  • @Tanner:嗯。是的,setattr(x, s, 42) 确实是正确的语法。令人惊讶的是花了这么长时间才发现这个错误。无论如何,关键是getattrsetattrexec 的替代品,当您只想获取任意成员,按字符串查找时。
【解决方案2】:

在示例中,使用 exec 函数将字符串作为代码执行。

import sys
import StringIO

# create file-like string to capture output
codeOut = StringIO.StringIO()
codeErr = StringIO.StringIO()

code = """
def f(x):
    x = x + 1
    return x

print 'This is my output.'
"""

# capture output and errors
sys.stdout = codeOut
sys.stderr = codeErr

exec code

# restore stdout and stderr
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__

print f(4)

s = codeErr.getvalue()

print "error:\n%s\n" % s

s = codeOut.getvalue()

print "output:\n%s" % s

codeOut.close()
codeErr.close()

【讨论】:

  • 像这样交换 stdout 和 stderr 让我很紧张。这看起来可能会导致巨大的安全问题。有办法解决吗?
  • @Narcolapser 您应该更关心使用exec(除非您知道代码字符串来自受信任的来源)。
【解决方案3】:

evalexec 是正确的解决方案,它们可以以更安全的方式使用。

正如在Python's reference manual 中讨论并在this 教程中明确解释的那样,evalexec 函数采用两个额外参数,允许用户指定可用的全局和局部函数和变量。

例如:

public_variable = 10

private_variable = 2

def public_function():
    return "public information"

def private_function():
    return "super sensitive information"

# make a list of safe functions
safe_list = ['public_variable', 'public_function']
safe_dict = dict([ (k, locals().get(k, None)) for k in safe_list ])
# add any needed builtins back in
safe_dict['len'] = len

>>> eval("public_variable+2", {"__builtins__" : None }, safe_dict)
12

>>> eval("private_variable+2", {"__builtins__" : None }, safe_dict)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'private_variable' is not defined

>>> exec("print \"'%s' has %i characters\" % (public_function(), len(public_function()))", {"__builtins__" : None}, safe_dict)
'public information' has 18 characters

>>> exec("print \"'%s' has %i characters\" % (private_function(), len(private_function()))", {"__builtins__" : None}, safe_dict)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'private_function' is not defined

本质上,您是在定义将在其中执行代码的命名空间。

【讨论】:

  • 不可能使 eval 安全:Eval really is dangerous。如果您从我这里获取代码并对其进行评估,我可以对您的 Python 程序进行分段。游戏结束。
  • @v.oddou 我在回应艾伦的声明,“eval 和 exec .. 可以以安全的方式使用。”这是错误的。如果有人说“可以以安全的方式使用 bash”,那也是错误的。巴什是危险的。这是必要的危险,但也很危险。声称 eval 可以变得安全是完全错误的。
  • @NedBatchelder 确实如此。你指向的链接是很好的材料。权力伴随着责任,所以关键是要意识到 eval 的潜在力量。如果我们决定 power=danger。
  • @NedBatchelder 许多用 Python 编写的代码也可能很危险,但你为什么假设 evalexec 打算用作 exec(input("Type what you want"))?在很多情况下,程序可能会根据计算结果编写过程或函数。生成的函数将与编写良好的程序的任何其他部分一样安全和快速(一旦评估)。 包含exec 的不安全程序并不比不安全程序本身造成的损害更危险,因为exec 不会赋予程序任何新权限。
  • @ThomasBaruchel 再次,我的观点是反驳 eval 或 exec 可以变得安全的概念。特别是,这个答案声称控制全局变量和局部变量将使安全使用它们成为可能。那是错误的。每当您使用 exec 和 eval 时,您都必须准确地知道正在执行的代码。如果你不这样做,那么你就会面临危险的操作。
【解决方案4】:

请记住,从版本 3 开始,exec 是一个函数!
所以请始终使用exec(mystring) 而不是exec mystring

【讨论】:

    【解决方案5】:

    避免使用execeval

    在 Python 中使用 execeval 是非常不受欢迎的。

    有更好的选择

    从最上面的答案(强调我的):

    对于语句,请使用exec

    当您需要表达式的值时,请使用eval

    但是,第一步应该是问问自己是否真的需要。 执行代码一般应该是不得已的位置:如果它可以包含用户输入的代码,那就是缓慢、丑陋和危险的。您应该始终首先查看替代方案,例如高阶函数,看看它们是否能更好地满足您的需求。

    来自Alternatives to exec/eval?

    使用字符串中的名称设置和获取变量的值

    [虽然eval] 可以工作,但通常不建议使用对程序本身有意义的变量名。

    相反,最好使用字典。

    这不是惯用的

    来自http://lucumr.pocoo.org/2011/2/1/exec-in-python/(强调我的)

    Python 不是 PHP

    不要试图绕过 Python 习语,因为其他语言的做法不同。命名空间出现在 Python 中是有原因的,仅仅因为它为您提供了工具 exec,并不意味着您应该使用该工具。

    很危险

    来自http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html(强调我的)

    所以 eval 是不安全的,即使你删除了所有的全局变量和内置函数!

    所有这些保护 eval() 的尝试的问题在于它们是黑名单。他们明确地删除了可能是危险的东西。这是一场失败的战斗,因为如果列表中只剩下一项,你就可以攻击系统

    那么,可以使 eval 安全吗?很难说。在这一点上,我最好的猜测是,如果你不能使用任何双下划线,你就不会造成任何伤害,所以如果你排除任何带有双下划线的字符串,你是安全的。也许……

    很难阅读和理解

    来自http://stupidpythonideas.blogspot.it/2013/05/why-evalexec-is-bad.html(强调我的):

    首先,exec 使人类更难阅读您的代码。为了弄清楚发生了什么,我不仅要阅读您的代码,我还必须阅读您的代码,弄清楚它将生成什么字符串,然后阅读该虚拟代码。所以,如果你在一个团队中工作,或者发布开源软件,或者在 StackOverflow 之类的地方寻求帮助,你会让其他人更难帮助你。如果有任何机会在 6 个月后对这段代码进行调试或扩展,那你直接让自己变得更难了。

    【讨论】:

    • “我最好的猜测是,如果你不能使用任何双下划线,你就不会造成任何伤害” - 你可以构造一个包含双下划线的字符串,然后调用 eval 该字符串。跨度>
    • 好建议,除非你正在编写代码生成器、工作运行程序或类似的东西......这里的大多数人可能正在这样做。
    • 我必须从配置文件 (cfg.yaml) 中导入相对路径:reldir : ../my/dir/ reldir = cfg[reldir]。但是,由于这个 python 代码应该在 Windows 和 Linux 上运行,我需要它来适应不同的操作系统路径分隔符; \\ /。所以我在配置文件中使用reldir : os.path.join('..','my','dir')。但这只会导致reldir 成为这个文字字符串,而不是被评估,所以我无法在reldir 中打开文件。你有什么建议吗?
    • 嗯,不。这些是高度pythonic。你认为 REPL 是什么?
    【解决方案6】:

    eval() 仅用于表达式,而 eval('x+1') 有效,eval('x=1') 将不起作用。在这种情况下,最好使用exec,甚至更好:尝试找到更好的解决方案:)

    【讨论】:

      【解决方案7】:

      您使用 exec 来完成代码的执行,就像下面的 IDLE 会话一样:

      >>> kw = {}
      >>> exec( "ret = 4" ) in kw
      >>> kw['ret']
      
      4
      

      【讨论】:

      • 这在普通 python 中不起作用。至少在 python 3 中没有。
      【解决方案8】:

      值得一提的是,'exec 的兄弟存在,如果你想调用一个 python 文件,它也叫execfile。如果您在包含糟糕 IDE 的第三方包中工作并且您想在其包之外进行编码,这有时会很好。

      示例:

      execfile('/path/to/source.py)'

      或:

      exec(open("/path/to/source.py").read())

      【讨论】:

      • “IDE”即“文本编辑器”?你不是说“API”吗?
      【解决方案9】:

      正如其他人提到的,它是“执行”..

      但是,如果您的代码包含变量,您可以使用“全局”来访问它,也可以防止编译器引发以下错误:

      NameError:名称“p_variable”未定义

      exec('p_variable = [1,2,3,4]')
      global p_variable
      print(p_variable)
      

      【讨论】:

        【解决方案10】:

        使用eval

        【讨论】:

        • Eval() 不执行语句。
        【解决方案11】:

        查看eval:

        x = 1
        print eval('x+1')
        ->2
        

        【讨论】:

        • Eval() 不执行语句。
        【解决方案12】:

        我尝试了很多东西,但唯一有效的是:

        temp_dict = {}
        exec("temp_dict['val'] = 10") 
        print(temp_dict['val'])
        

        输出:

        10

        【讨论】:

        • 在尝试了上述大部分方法之后,这是唯一有效的方法。格拉西亚斯
        【解决方案13】:

        最合乎逻辑的解决方案是使用内置的eval() 函数。另一种解决方案是将该字符串写入临时python 文件并执行它。

        【讨论】:

          【解决方案14】:

          好的.. 我知道这并不完全是一个答案,但可能是给像我一样看这个的人的一个注释。我想为不同的用户/客户执行特定的代码,但也想避免 exec/eval。我最初希望为每个用户将代码存储在数据库中并执行上述操作。

          我最终在“customer_filters”文件夹中的文件系统上创建文件并使用“imp”模块,如果没有为该客户应用过滤器,它就会继续

          import imp
          
          
          def get_customer_module(customerName='default', name='filter'):
              lm = None
              try:
                  module_name = customerName+"_"+name;
                  m = imp.find_module(module_name, ['customer_filters'])
                  lm = imp.load_module(module_name, m[0], m[1], m[2])
              except:
                  ''
                  #ignore, if no module is found, 
              return lm
          
          m = get_customer_module(customerName, "filter")
          if m is not None:
              m.apply_address_filter(myobj)
          

          所以 customerName = "jj" 将从 customer_filters\jj_filter.py 文件中执行 apply_address_filter

          【讨论】:

          • 您是如何管理安全的?您怎么知道客户不会滥用此特权?
          猜你喜欢
          • 2012-07-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-09-11
          • 2020-06-26
          相关资源
          最近更新 更多