该__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
代码这些年来一直在逐步重构,所以此时你几乎可以eval
用ast
模块和朋友用几行纯Python重新实现,或者用函数用几行C语言重新实现PyEval*
,所以很难指出您可以在不知道您关心的实现和版本的情况下从确切的代码行开始。