2013-02-20 102 views
3

earlier post我詢問方法,以避免在中間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 

蜜蜂在我對這個模式引擎蓋主要來自一個嚮往誠實,善良詞法範圍,就不會連經過多年編程的Python死亡。在Python 我湊合着用顯式調用del

它發生,我認爲這可能可以使用上下文管理器模仿詞彙範圍在Python。這將是這個樣子:

with my(df.xs('A')['II'] - df.xs('B')['II']) as tmp: 
    result = tmp[tmp < 0] 

爲了能夠模仿詞彙範圍,上下文管理器類需要有一種方法來del ETE是被分配返回的值在呼叫範圍內的變量及其(上下文管理器的'輸入'的方法。

例如,對於作弊的大劑量的:

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語句的末尾替換它們。


例如,在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聲明,而且還提供了控制範圍的各種其他方式,包括詞法和動態) 。

我不需要提醒大家,只有一小部分Python程序員給出了一個關於詞法範圍的老鼠尾巴。

+0

看起來很像[獲取要在with語句中執行的命令塊](http://stackoverflow.com/q/12485837) – 2013-02-20 20:03:14

+0

我不認爲你可以,至少不是沒有得到深入黑客和實施細節領域,這是不值得的。順便說一句,Python做詞彙範圍確定,你只是想要一個更細緻的方式來引入新的範圍。 – delnan 2013-02-20 20:10:18

+0

首先,在JS中,你只是定義一個本地函數並調用它,它允許你使用本地函數的作用域。你可以在Python中完成同樣的事情。那麼,這有什麼問題?你是否反對賦予這個函數一個名字? – abarnert 2013-02-20 20:10:29

回答

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聲明。 (一些不純的語言允許你在let內綁定新的名字,這些新名字將在let以外生活,並且你可以將它描述爲一個隱含的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] 

沒有理由推式進with聲明,並嘗試找出它變得邊界至。

相關問題