Eval不仅包含词典

时间:2013-05-03 21:23:59

标签: python dictionary

normaly我可以像这样使用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就可以简单地调用[n for m in mylist (查询,Dummy(n)]和`query =“color =='yellow'”。我不是专家,但我只是想知道这种方法是否会导致问题。

2 个答案:

答案 0 :(得分:2)

__getitem__dict用于按键检索项目的内容。通过覆盖__getitem__并使其返回self.node的属性,您基本上将node转换为字典。

一种更简单的方法就是使用__dict__属性,它可以做同样的事情:

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

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

答案 1 :(得分:2)

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

序列和映射的参考文档位于Data model,但最好的起点可能是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(查询,则可以简单地在mylist中调用n作为n) ,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方法是如何实现的,因此代码可能会在将来出现一些奇怪的错误?

不,那几乎从不问题。

首先,documentation for eval通常足以准确预测它将做什么,并且所有Python实现都必须遵循该文档。

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


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

** eval代码多年来逐渐被重构,所以此时你可以通过使用eval模块在​​几行纯Python中重新实现ast和朋友,或使用PyEval*函数的几行C,所以很难指出你在不知道你关心的实现和版本的情况下开始的确切代码行。