eval在做什么?

时间:2019-04-02 10:27:39

标签: python eval

尝试完成Codesignal上的“可逆括号”挑战约2天,并提出了150多个行无效的代码后,我偶然发现了以下使用eval()的代码。

现在,我了解eval()接受一个字符串并将其解释为好像已将其输入到控制台中。但是我真的不明白它是如何实现其目标的。有人可以一点一点地解释一下发生了什么吗?

谢谢

我尝试使用谷歌搜索,阅读文档并搜索youtube以获得更好的理解。但无济于事。

def reverseInParentheses(s):
    return eval('"' + s.replace('(', '"+("').replace(')', '")[::-1]+"') + '"')
 inputString = "(bar)"
 reverseInParentheses(inputString)  # output: "rab"

 inputString = "foo(bar)baz"
 reverseInParentheses(inputString)  # output: "foorabbaz"

 inputString = "foo(bar)baz(blim)"
 reverseInParentheses(inputString)  # output: "foorabbazmilb"

 inputString = "foo(bar(baz))blim"
 reverseInParentheses(inputString)  # output: "foobazrabblim"

2 个答案:

答案 0 :(得分:1)

逐步

eval('"' + s.replace('(', '"+("').replace(')', '")[::-1]+"') + '"')
  • '"'字符串的开头
  • s.replace('(', '"+("')(替换字符'"+("',本质上就是将字符串拆分为前一个+后一个。
  • replace(')', '")[::-1]+"'))替换字符)[::-1]+,将括号之间的字符串反转,并添加以下内容。确实,'abc'[::-1] = 'cba'
  • + '"'关闭字符串。

通过这种方式,“解析器”将每个字符串分隔在方括号中,将其反转,然后添加以下字符串。

答案 1 :(得分:1)

让我们一一剖析:

eval('"' + s.replace('(', '"+("').replace(')', '")[::-1]+"') + '"')

这实际上与以下内容相同(这可能更易于理解):

code = '"' + s.replace('(', '"+("').replace(')', '")[::-1]+"') + '"'
eval(code)

与以下相同:

innerCode = s.replace('(', '"+("').replace(')', '")[::-1]+"')
code = '"' + innerCode + '"'
eval(code)

innerCode在此处进行简单的字符串操作:

  • (替换为"+("
  • )替换为")[::-1]+"

因此,以您的示例(bar)为例,结果如下:"+("bar")[::-1]+"

如果再次添加引号,则会得到以下字符串:

  ""+("bar")[::-1]+""
= ("bar")[::-1]
= "bar"[::-1]

[::-1]对字符串所做的操作与之相反,实质上是从背面进行迭代(这就是-1所做的事情):

>>> 'foo'[::-1]
'oof'
>>> 'bar'[::-1]
'rab'

然后用eval执行生成的代码时,您会得到结果。

让我们看另一个示例:foo(bar)baz(blim)。替换括号后,您将得到以下信息:

foo"+("bar")[::-1]+"baz"+("blim")[::-1]+"

添加引号并将其简化,您将获得以下信息:

  "foo"+("bar")[::-1]+"baz"+("blim")[::-1]+""
= "foo" + ("bar")[::-1] + "baz" + ("blim")[::-1]
= "foo" + "bar"[::-1] + "baz" + "blim"[::-1]

执行该操作时,您会得到"foo" + "rab" + "baz" + "milb"


请注意,尽管这样做可以解决问题,但是使用eval实际上是一个非常糟糕的主意。 eval执行任何代码,而不仅仅是字符串连接和字符串反转。因此,如果您从盲目信任的来源获取输入,则攻击者可能会以此来执行错误的代码。

最好完全不使用eval来实现此行为,这并不是 困难,因为您毕竟只是在操纵字符串。

例如,使用正则表达式快速查找括号:

import re
def reverseInParentheses(s):
    for m in re.findall('\((.*?)\)', s):
        s = s.replace('(' + m + ')', m[::-1])
    return s
>>> reverseInParentheses("(bar)")
'rab'
>>> reverseInParentheses("foo(bar)baz")
'foorabbaz'
>>> reverseInParentheses("foo(bar)baz(blim)")
'foorabbazmilb'
>>> reverseInParentheses("foo(bar(baz))blim")
'foozab(rab)blim'

请注意,这对于带有嵌套括号的最后一个示例不正确。对于这种情况,最好使用适当的解析器,例如pyparsingthis answer on a different question中对此进行了更详细的描述。

我强烈建议您不要在这里使用eval,即使它确实适合您的情况。