【发布时间】:2013-02-21 06:28:49
【问题描述】:
在earlier post 中,我询问了在以下模式中避免中间tmp 变量的方法:
tmp = <some operation>
result = tmp[<boolean expression>]
del tmp
...tmp 是 pandas 对象。例如:
tmp = df.xs('A')['II'] - df.xs('B')['II']
result = tmp[tmp < 0]
del tmp
我对这种模式的看法基本上来自对诚实至善的词法作用域的渴望1,即使经过多年的 Python 编程,它也不会消亡。在 Python2 中,我使用对 del 的显式调用,
在我看来,可能可以使用上下文管理器来模拟 Python 中的词法作用域。它看起来像这样:
with my(df.xs('A')['II'] - df.xs('B')['II']) as tmp:
result = tmp[tmp < 0]
为了能够模拟词法作用域,上下文管理器类需要有一种方法来delete 调用作用域中的变量,该变量被分配了由其(上下文管理器的)返回的值 'enter '方法。
例如,大量作弊:
import contextlib as cl
# herein lies the rub...
def deletelexical():
try: del globals()['h']
except: pass
@cl.contextmanager
def my(obj):
try: yield obj
finally: deletelexical()
with my(2+2) as h:
print h
try:
print h
except NameError, e:
print '%s: %s' % (type(e).__name__, e)
# 4
# Name error: name 'h' is not defined
当然,问题是要真正实现deletelexical。可以吗?
编辑:正如 abarnert 指出的那样,如果在周围的范围内已经存在一个 tmp,deletelexical 将不会恢复它,因此它几乎不能被视为词法范围的模拟。正确的实现必须将任何现有的 tmp 变量保存在周围范围内,并在 with 语句的末尾替换它们。
1例如,在 Perl 中,我会将上面的代码编写为:
my $result = do {
my $tmp = $df->xs('A')['II'] - $df->xs('B')['II'];
$tmp[$tmp < 0]
};
或在 JavaScript 中:
var result = function () {
var tmp = df.xs('A')['II'] - df.xs('B')['II'];
return tmp[tmp < 0];
}();
编辑:回应 abarnert 的帖子和评论:是的,在 Python 中可以定义
def tmpfn():
tmp = df.xs('A')['II'] - df.xs('B')['II']
return tmp[tmp < 0]
...这确实可以防止命名空间与以后无用的名称tmp 混淆,但它是通过将命名空间与以后的无用名称tmpfn 混淆来实现的。 JavaScript(还有 Perl,顺便说一句,等等)允许 匿名 函数,而 Python 不允许。无论如何,我认为 JavaScript 的匿名函数是获取词法作用域的一种有点麻烦的方法。它肯定比没有好,而且我大量使用它,但它远不如 Perl 的好(后者我的意思不仅是 Perl 的 do 语句,还有它提供的控制范围的各种其他方式,包括词汇和动态)。
2我不需要提醒这样一个事实,即只有极少数的 Python 程序员对词法作用域嗤之以鼻。
【问题讨论】:
-
我认为你做不到,至少如果不深入到 hack-and-implementation-details 领域,那是不值得的。顺便说一句,Python 做了词法作用域,你只需要一种更细粒度的方式来引入新的作用域。
-
首先,在 JS 中,你只是定义了一个本地函数并调用它,它允许你使用本地函数的作用域。你可以在 Python 中做同样的事情。那么,这有什么问题呢?你反对给函数命名吗?
-
第二,你展示了一个 perl 和 JS 语法的例子,它释放了 within 范围内定义的所有内容,然后尝试构建 Python 语法,让所有定义在范围内单独,但释放范围之外定义的特定事物。它更像是一个
let语句,其中包含一个隐式的nonlocal everything-else,或者一个 C++ 范围保护宏,而不是一个真正的词法范围。这真的是你想要的吗? (另外,如果您在此with语句之前已经有一个名为tmp的全局变量会发生什么?如果您有一个名为tmp的 local 会发生什么?) -
这似乎是一个刚刚恢复
globals和/或locals的上下文管理器(或者可能只是从其中一个/两个中删除了任何新定义的名称,同时允许已经存在但反弹保持反弹的名字?)更适合您的要求,并且可能没有那么难。或者,更简单地说,一种使用临时globals和/或locals(空、从周围复制或显式传入)执行一些任意代码的方法?
标签: python