在之前的一篇文章中,我询问了如何避免tmp
模式中的中间变量,例如:
tmp = <some operation>
result = tmp[<boolean expression>]
del tmp
...tmp
熊猫对象在哪里。例如:
tmp = df.xs('A')['II'] - df.xs('B')['II']
result = tmp[tmp < 0]
del tmp
我对这种模式的看法基本上来自对诚实至善的词法作用域1的渴望,即使经过多年的 Python 编程,它也不会消亡。在 Python 2中,我通过显式调用del
,
我突然想到可以使用上下文管理器来模拟 Python 中的词法作用域。它看起来像这样:
with my(df.xs('A')['II'] - df.xs('B')['II']) as tmp:
result = tmp[tmp < 0]
为了能够模拟词法作用域,上下文管理器类需要有一种方法来del
获取调用作用域中的变量,该变量被分配了由其(上下文管理器的)“输入”方法返回的值。
例如,大量的作弊行为:
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
真正实施。可以做到吗?
tmp
编辑:正如 abarnert 指出的那样,如果周围范围中存在预先存在,则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,BTW 等)允许匿名函数,而 Python 不允许。无论如何,我认为 JavaScript 的匿名函数是获取词法作用域的一种有点麻烦的方法。它肯定比没有好,我大量使用它,但它远不如 Perl 的好(我所说的后者不仅是指 Perl 的do
声明,还包括它提供的各种其他方式来控制范围,包括词汇和动态)。
2我不需要提醒这样一个事实,即只有极少数的 Python 程序员对词法作用域持反对意见。