问题:为什么使用“评估”是一种不好的做法?
我正在使用以下类轻松存储我的歌曲的数据。
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似乎被认为是不良做法,使用不安全。如果是这样,有人可以向我解释原因并向我展示定义上述类的更好方法吗?
回答 0
是的,使用eval是一种不好的做法。仅出于以下几个原因:
- 几乎总有一种更好的方法
- 非常危险和不安全
- 使调试困难
- 慢
您可以使用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)编辑:
在某些情况下,您必须使用eval或exec。但是它们很少见。当然,在您的情况下使用eval是一个不好的做法。我要强调不好的做法,因为eval和exec经常在错误的地方使用。
编辑2:
似乎有些不同意,在OP案件中,评估是“非常危险和不安全的”。对于这种特定情况,这可能是正确的,但一般而言并非如此。问题是一般性的,我列出的理由也适用于一般性情况。
编辑3: 重新排序的点1和4
回答 1
使用eval是很弱的,不是一个明显的坏习惯。
- 它违反了“软件基本原理”。您的来源不是可执行文件的总和。除了您的资料来源外,还 - eval必须清楚地了解到的参数。因此,它是万不得已的工具。
- 通常,这是经过漫长设计的标志。动态构建动态源代码的理由很少。委托和其他OO设计技术几乎可以完成任何事情。 
- 这会导致相对缓慢的小代码即时编译。通过使用更好的设计模式可以避免开销。 
作为注脚,在精神错乱的社会主义者的手中,这可能效果不佳。但是,当遇到精神错乱的用户或管理员时,最好不要首先让他们理解Python。在真正的邪恶之手,Python可以承担责任。eval完全不会增加风险。  
回答 2
回答 3
是的:
使用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]"回答 4
值得注意的是,对于有问题的特定问题,可以使用eval以下几种替代方法:
如上所述,最简单的方法是使用setattr:
def __init__(self):
    for name in attsToStore:
        setattr(self, name, None)一种不太明显的方法是__dict__直接更新对象的对象。如果您要做的只是将属性初始化为None,那么这比上面的方法要简单。但是考虑一下:
def __init__(self, **kwargs):
    for name in self.attsToStore:
       self.__dict__[name] = kwargs.get(name, None)这使您可以将关键字参数传递给构造函数,例如:
s = Song(name='History', artist='The Verve')它还允许您locals()更加明确地使用它,例如:
s = Song(**locals())…并且,如果您确实要分配None名称的属性,请在中找到locals():
s = Song(**dict([(k, None) for k in locals().keys()]))为对象提供属性列表默认值的另一种方法是定义类的__getattr__方法:
def __getattr__(self, name):
    if name in self.attsToStore:
        return None
    raise NameError, name如果无法以常规方式找到named属性,则调用此方法。这种方法比简单地在构造函数中设置属性或更新的方式要简单一些__dict__,但是它的优点是除非存在该属性,否则不实际创建该属性,这样可以大大减少类的内存使用量。
所有这些的要点:通常有很多原因可以避免:避免eval执行无法控制的代码的安全性问题,无法调试的代码的实际问题等。但是,更重要的原因是通常,您不需要使用它。Python向程序员公开了很多内部机制,因此您几乎不需要编写编写代码的代码。
回答 5
其他用户指出了如何可以更改不依赖的代码eval; 我将提供一个使用的合法用例eval,即使在CPython中也可以找到一个用例:testing。
这是我在test_unary.py其中测试是否(+|-|~)b'a'引发的一个示例TypeError:
def test_bad_types(self):
    for op in '+', '-', '~':
        self.assertRaises(TypeError, eval, op + "b'a'")
        self.assertRaises(TypeError, eval, op + "'a'")显然,这里的用法不是坏习惯;您定义输入,仅观察行为。eval方便测试。
看看这个搜索在为eval,在CPython的Git仓库中进行; 大量使用eval进行测试。
回答 6
什么时候 eval()用于处理用户提供的输入时,您使用户能够拖放到提供以下内容:
"__import__('code').InteractiveConsole(locals=globals()).interact()"您可以摆脱它,但是通常您不希望向量在您的应用程序中执行任意代码。
回答 7
除了@Nadia Alramli答案之外,由于我是Python的新手,并且渴望检查使用eval将如何影响计时,因此我尝试了一个小程序,以下是观察结果:
#Difference while using print() with eval() and w/o eval() to print an int = 0.528969s per 100000 evals()
from datetime import datetime
def strOfNos():
    s = []
    for x in range(100000):
        s.append(str(x))
    return s
strOfNos()
print(datetime.now())
for x in strOfNos():
    print(x) #print(eval(x))
print(datetime.now())
#when using eval(int)
#2018-10-29 12:36:08.206022
#2018-10-29 12:36:10.407911
#diff = 2.201889 s
#when using int only
#2018-10-29 12:37:50.022753
#2018-10-29 12:37:51.090045
#diff = 1.67292
