我正在使用以下课程轻松存储我的歌曲数据。
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 :(得分:170)
是的,使用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情况下,eval“非常危险且不安全”。对于这个特定情况可能也是如此,但一般情况下并非如此。这个问题很笼统,我列出的原因也适用于一般情况。
编辑3: 重新排序第1和第4点
答案 1 :(得分:28)
使用eval
很弱,而不是明确糟糕的练习。
违反了“软件基本原理”。您的来源不是可执行文件的总和。除了您的来源之外,还有eval
的参数,必须清楚地理解这些参数。因此,它是最后的工具。
这通常是轻率设计的标志。动态源代码很少有充分的理由,即时构建。使用委托和其他OO设计技术几乎可以做任何事情。
导致小块代码的动态编译相对较慢。通过使用更好的设计模式可以避免开销。
作为一个脚注,在疯狂的反社会人士的手中,它可能不会很好。然而,当面对精神错乱的反社会用户或管理员时,最好不要首先给他们解释Python。在真正邪恶的手中,Python可以承担责任; eval
根本不会增加风险。
答案 2 :(得分:22)
答案 3 :(得分:13)
是的,它是:
使用Python进行Hack:
css
以下代码将列出在Windows计算机上运行的所有任务。
>>> eval(input())
"__import__('os').listdir('.')"
...........
........... #dir listing
...........
在Linux中:
>>> eval(input())
"__import__('subprocess').Popen(['tasklist'],stdout=__import__('subprocess').PIPE).communicate()[0]"
答案 4 :(得分:6)
值得注意的是,针对具体问题,使用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
当以正常方式找不到命名属性时,将调用此方法。这种方法不如简单地在构造函数中设置属性或更新__dict__
那么简单,但它的优点是实际上不创建属性,除非它存在,这可以大大减少类的内存使用。
关键所在:一般来说,有很多原因可以避免eval
- 执行无法控制的代码的安全问题,无法调试的代码的实际问题,但更重要的原因是,一般来说,你不需要使用它。 Python向程序员公开了很多内部机制,你很少需要编写编写代码的代码。
答案 5 :(得分: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
的Take a look at this search,在CPython git存储库上执行;使用eval进行大量测试。
答案 6 :(得分:2)
当eval()
用于处理用户提供的输入时,您可以让用户Drop-to-REPL提供如下内容:
"__import__('code').InteractiveConsole(locals=globals()).interact()"
你可能会侥幸成功,但通常你不想在你的应用程序中使用arbitrary code execution的向量。
答案 7 :(得分:0)
除了@Nadia Alramli答案之外,由于我是Python的新手,并渴望检查使用eval
会如何影响 timings ,我尝试了一个小程序,以下是观察结果:
#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