0

通常我可以像这样使用 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 正在使用__getitem__new_dict 上的方法来获取颜色的?我找到了文档和 python 帮助功能,但是我找不到方法(或实际代码)的分步文档,所以我永远不会想到去做那件事上面的代码可以。还是使用上述方法不好,因为没有人真正知道 eval 方法是如何实现的,因此代码将来可能会出现一些奇怪的错误?

编辑:这就是程序中使用 eval 的原因:假设您在 mylist 列表中有 20 个 myStuff 对象,并且您想按黄色过滤它们,那么可以简单地调用 [ n for n in mylist if eval(query, Dummy(n) ] with `query="color=='yellow'"。我不是专家,但我只想知道这种方法是否会导致问题。

4

2 回答 2

2

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

一个更简单的方法是只使用__dict__属性,它做同样的事情:

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

但实际上,不要使用这个。请。eval()很少是适合这项工作的工具,这是您不需要使用eval.

于 2013-05-03T21:29:12.420 回答
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 对象,你想用黄色过滤它们,那么可以简单地调用 [ n for n in mylist if 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代码这些年来一直在逐步重构,所以此时你几乎可以evalast模块和朋友用几行纯Python重新实现,或者用函数用几行C语言重新实现PyEval*,所以很难指出您可以在不知道您关心的实现和版本的情况下从确切的代码行开始。

于 2013-05-03T21:30:55.907 回答