在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编程,它也不会死亡。在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
。可以吗?
编辑:正如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的帖子&amp;评论:是的,在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程序员会给出一个关于词法范围的大鼠尾巴。
答案 0 :(得分:3)
在等效的JavaScript中,您可以这样做:
var result = function () {
var tmp = df.xs('A')['II'] - df.xs('B')['II'];
return tmp[tmp < 0];
}();
换句话说,为了获得额外的词法范围,您将创建一个新的本地函数并使用其范围。你可以在Python中做同样的事情:
def tmpf():
tmp = df.xs('A')['II'] - df.xs('B')['II']
return tmp[tmp < 0]
result = tmpf()
它具有完全相同的效果。
这种影响并不是你认为的那样。超出范围只意味着它可以被收集的垃圾。这正是一个真正的词汇范围会给你的东西,但它不是你想要的(一种在某些时候确定性地破坏某些东西的方法)。是的,它通常在CPython 2.7中做你想要的,但这不是语言功能,它是一个实现细节。
但是,只需使用一个函数,你的想法就会在问题上增加一些问题。
您的想法会在with
语句中保留所有已定义或反弹的内容。 JS等价物不会那样做。你所谈论的更像是C ++范围保护宏,而不是let
语句。 (一些不纯的语言允许你set!
- 绑定let
中将存在于let
之外的新名称,你可以将其描述为具有隐式{{1}的词法范围在体内,但它仍然很奇怪。特别是在一种已经在重新绑定和变异之间有很大区别的语言中。)
此外,如果您已经拥有同名nonlocal everything-but-the-let-names
的全局,则此tmp
语句会将其删除。这不是with
语句或任何其他常见的词法范围形式。 (如果let
是局部变量而不是全局变量,那该怎么办?)
如果要使用上下文管理器模拟词法作用域,您真正需要的是在退出时恢复tmp
和/或globals
的上下文管理器。或者只是在临时locals
和/或globals
中执行任意代码的方法。 (我不确定这是否可行,但你得到的想法 - 就像将locals
的主体作为with
对象并将其传递给code
。)
或者,如果要允许重新绑定以转义范围,而不是新绑定,请遍历exec
和/或globals
并删除所有新内容。
或者,如果您只想删除特定内容,只需编写locals
上下文管理器:
deleting
没有理由将表达式推入with deleting('tmp'):
tmp = df.xs('A')['II'] - df.xs('B')['II']
result = tmp[tmp < 0]
语句并尝试弄清楚它绑定的内容。