为什么使用 'eval' 是不好的做法?

189 投票
8 回答
79848 浏览
提问于 2025-04-15 16:36

我用下面这个类来方便地存储我的歌曲数据。

class Song:
    """The class to store the details of each song"""
    attsToStore=('Name', 'Artist', 'Album', 'Genre', 'Location')
    def __init__(self):
        for att in self.attsToStore:
            exec 'self.%s=None'%(att.lower()) in locals()
    def setDetail(self, key, val):
        if key in self.attsToStore:
            exec 'self.%s=val'%(key.lower()) in locals()

我觉得这样比写一大堆if/else语句要灵活多了。不过,我听说eval不安全。真的是这样吗?有什么风险呢?我该怎么解决我这个类里的问题(动态设置self的属性),又不冒这个风险呢?

8 个回答

28

是的,确实可以:

用Python来搞定:

>>> eval(input())
"__import__('os').listdir('.')"
...........
...........   #dir listing
...........

下面的代码会列出在Windows电脑上运行的所有任务。

>>> eval(input())
"__import__('subprocess').Popen(['tasklist'],stdout=__import__('subprocess').PIPE).communicate()[0]"

在Linux系统上:

>>> eval(input())
"__import__('subprocess').Popen(['ps', 'aux'],stdout=__import__('subprocess').PIPE).communicate()[0]"
51

使用 eval 并不是一个明显的“坏”做法,但它确实有一些缺点。

  1. 它违反了“软件的基本原则”。你的源代码并不是唯一可以执行的内容。除了你的源代码,还有传给 eval 的参数,这些参数必须被清楚理解。因此,eval 只应该在最后的情况下使用。

  2. 这通常是设计不周的表现。动态生成源代码的情况很少有好的理由。几乎所有的事情都可以通过委托和其他面向对象的设计方法来实现。

  3. 它会导致小段代码的即时编译速度相对较慢。这种额外的开销可以通过更好的设计模式来避免。

顺便提一下,在一些极端不理智的人手中,使用 eval 可能会带来不好的结果。不过,如果你遇到这些极端不理智的用户或管理员,最好根本就不要给他们解释型的 Python。在真正邪恶的人手中,Python 可能会变得危险;而 eval 并不会增加这种风险。

250

是的,使用 eval 是一种不好的做法。这里有几个原因:

  1. 几乎总有更好的方法可以实现同样的功能
  2. 非常危险且不安全
  3. 让调试变得困难
  4. 运行速度慢

在你的情况下,可以使用 setattr 来代替:

class Song:
    """The class to store the details of each song"""
    attsToStore=('Name', 'Artist', 'Album', 'Genre', 'Location')
    def __init__(self):
        for att in self.attsToStore:
            setattr(self, att.lower(), None)
    def setDetail(self, key, val):
        if key in self.attsToStore:
            setattr(self, key.lower(), val)

有些情况下你确实需要使用 evalexec,但这些情况很少见。在你的情况下,使用 eval 绝对是不好的做法。我强调这是不好的做法,因为 evalexec 经常在错误的地方被使用。

回复评论:

看起来有些人不同意在这个特定情况下 eval 是“非常危险且不安全”的说法。这可能在这个特定情况下是对的,但一般来说并不是。问题是比较普遍的,我列出的理由在一般情况下也是成立的。

撰写回答