繁体   English   中英

评估不仅限于字典

[英]Eval not only with dictionaries

通常,我可以这样使用eval:

new_dict={'color':'green'}
eval("color=='green'",new_dict)

在这种情况下,它将返回true,因此我知道new_dict中的颜色实际上是绿色。 现在,我找到了一些代码,其中有人想要使用eval,但是使用了更通用的对象而不是dict。 在下面的代码中基本上是此人的工作:

class myStuff(object):
    def __init__(self):
        self.color = "green"

class Dummy(dict):
    def __init__(self, node):
        dict.__init__(self)
        self.node = node

    def __getitem__(self, key):
        return(getattr(self.node, key))


node=myStuff()
new_dict = Dummy(node)
print eval("color=='green'",new_dict)

我现在想知道-上面代码的开发人员如何知道eval在new_dict上将__getitem__方法用于颜色? 我找到了文档和python帮助功能,但是我找不到关于方法(或实际代码)的分步文档,因此我永远也不会想出要做什么的想法。上面的代码呢。 还是使用上述方法不好,因为没有人真正知道eval方法是如何实现的,因此代码将来可能会出现一些奇怪的错误?

编辑:这就是为什么在程序中使用eval的原因:假设您在列表mylist中有20个myStuff对象,并且想要用黄色过滤它们,那么只要eval(query, Dummy(n)]和“ query =“ color =='yellow'”。我不是专家,但我只是想知道此方法是否会导致问题。

__getitem__dict用来通过其键检索项目的对象。 通过覆盖__getitem__并使其返回self.node的属性,您实际上可以将node变成字典。

一种更简单的方法是只使用__dict__属性,它执行相同的操作:

print eval("color=='green'", node.__dict__)

但实际上,不要使用它。 请。 eval()很少是适合该工作的工具,这是不需要使用eval的完美实例。

__getitem__函数是一种模拟字典或列表(或更准确地说,是任何映射或序列)的方法。

序列和映射的参考文档位于Data模型中 ,但是最好的起点可能是collections.abc模块及其中的链接。

总结基本思想,当您编写如下代码时:

foo[bar]

Python将其转换为*:

foo.__getitem__(bar)

定义__getitem__来模拟dict没有错。

并创建一个将其属性视为dict项的对象是一种常见的模式,它具有一个名称(“ attrdict”)。

但是,使用eval 几乎总是错误的做法。 因此,通常,做正确的事才能使eval工作正确,因为您正在做正确的事,但错误的是您首先使用eval


在您的特定情况下,没有充分的理由首先使用eval 代替这个:

eval("color=='green'",new_dict)

只要这样做:

new_dict['color']=='green'

Python新手(尤其是那些在PHP,Tcl或JavaScript的旧版本上长大的人)经常想要使用eval是获得一个可以轻松传递的表达式。 但是在Python(以及现代PHP和JS)中,函数是一流的值,就像字符串一样容易传递-而且,与字符串不同,它们是可调用的。 您可以创建命名或lambda函数,或使用partial ,关闭所需的任何局部变量等。

使用字符串几乎不能使用使用函数不能使用的字符串,当然,除了要打开一个巨大的安全漏洞,降低性能并阻碍调试之外,这几乎是没有的。

因此,而不是像这样:

expr = "color=='green'"
# ...
eval(expr, new_dict)

……只要这样做:

expr = lambda x: x.color=='green'
# ...
expr(new_dict)

在您编辑的问题中:

这就是为什么在程序中使用eval的原因:假设您在列表mylist中有20个myStuff对象,并且想用黄色过滤它们,那么只要eval(query,Dummy( n)]和“ query =“ color =='yellow'”。

因此,您大概正在执行以下操作:

query = "color=={}'.format(color)
# ...
[n for n in mylist if eval(query, Dummy(n)]

但是您可以轻松地做到这一点:

[n for n in mylist if n.color == color]

即使您需要更具动态性的内容,也可以比字符串更轻松地动态构建函数:

query = lambda n: n.color == color
[n for n in mylist if query(n)]

实际上,如果您确实愿意,甚至可以使此功能完全起作用:

filter(compose(partial(operator.eq, color), attrgetter('color')), mylist)

但是Python的优点在于,您不必完全发挥功能或完全必须执行命令,而可以编写介于两者之间的内容,即25%或75%,无论哪种方式都最容易读写。


与此同时:

还是使用上述方法不好,因为没有人真正知道eval方法是如何实现的,因此代码将来可能会出现一些奇怪的错误?

不,那几乎从来不是问题。

首先, eval文档通常足以准确预测其功能,并且所有Python实现都必须遵循该文档。

在您确实需要了解更多信息的极少数情况下,所有主要实现都是开源的,因此您只需阅读代码即可。 例如,您可以在此处在线浏览CPython 3.3代码。**


*这并不完全准确; 实际的代码实际上是在类而不是对象中查找__getitem__ (2.x中的旧类与新类稍有不同),并处理C模块/ Java包/适用于您的Python实现的扩展类型,切片(在2.x与3.x中不同)等。但这是基本思想。

** eval代码多年来已经逐步重构,因此,此时,您可以使用ast模块和好友在几行纯Python中重新实现eval ,或者使用PyEval*函数在几行C中重新实现eval ,因此,在不知道您关心的实现和版本的情况下,很难将您指向确切的代码行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM