2011-11-08 59 views
10

有時候,我發現我不得不使用功能名稱很長,如os.path.abspathos.path.dirname一個很多在短短的幾行代碼。我不認爲用這些函數亂扔全局名稱空間是值得的,但是能夠圍繞我需要這些函數的線定義一個範圍將是非常有幫助的。作爲一個例子,這將是完美的:我可以在Python中的任何位置定義範圍嗎?

import os, sys 

closure: 
    abspath = os.path.abspath 
    dirname = os.path.dirname 

    # 15 lines of heavy usage of those functions 

# Can't access abspath or dirname here 

我很想知道這是可行的莫名其妙

+0

我認爲「封閉」在這裏不是合適的詞。請參閱http://en.wikipedia.org/wiki/Closure_(computer_science)。也許「臨時命名空間」是你的意思? –

+1

@RaymondHettinger - 我認爲這個詞是'範圍'。 「我可以在Python中定義除函數,模塊全局或內置之外的範圍嗎?」 – Omnifarious

+0

通常,當你使用合理數量的代碼行編碼某個屬於某個函數或方法的東西時。因此,「拋棄全局命名空間」應該不是問題,或者您的代碼可能需要重新設計,而不是「範圍」。 – MatthieuW

回答

19

Python沒有像Lisp或計劃let臨時命名空間的工具。

Python中常用的技術是將名稱放在當前名稱空間中,然後在完成它們時將其取出。這種技術被大量使用標準庫:

abspath = os.path.abspath 
dirname = os.path.dirname 
# 15 lines of heavy usage of those functions 
a = abspath(somepath) 
d = dirname(somepath) 
... 
del abspath, dirname 

一種替代技術,以減少打字努力縮短重複前綴:

>>> import math as m 
>>> m.sin(x/2.0) + m.sin(x * m.pi) 

>>> p = os.path 
... 
>>> a = p.abspath(somepath) 
>>> d = p.dirname(somepath) 

在標準庫中常用的另一種方法是隻不擔心污染模塊命名空間,僅僅依靠__all__列出你打算公開其姓名。在docs for the import statement中討論了__all__的影響。

當然,你也可以通過在字典存儲名字(雖然這個解決方案是不常見的)創建自己的命名空間:

d = dict(abspath = os.path.abspath, 
     dirname = os.path.dirname) 
... 
a = d['abspath'](somepath) 
d = d['dirname'](somepath) 

最後,你可以把所有的代碼在一個函數(它有自己的本地命名空間),但是這有一些缺點:

  • 設置是尷尬(非典型和神祕的使用功能)
  • 需要聲明爲全球你想要做的任何任務都不是暫時的。
  • 代碼將無法運行,直到調用函數
def temp():      # disadvantage 1: awkward setup 
    global a, d      # disadvantage 2: global declarations 
    abspath = os.path.abspath 
    dirname = os.path.dirname 
    # 15 lines of heavy usage of those functions 
    a = abspath(somepath) 
    d = dirname(somepath) 
temp()        # disadvantage 3: invoking the code 
1

只是做一個功能?

def do_things_with_those_functions(): 
    abspath = os.path.abspath 
    dirname = os.path.dirname 
    # etc. 

您可以在任何範圍內做到這一點:

def outer_function(): 
    a = 5 
    def inner_function(): 
     print(a) 
    inner_function() 
+0

這種方法有效,但它有一個顯着的缺點,因爲在使用這些函數時,您必須聲明爲* global *有用的任何東西。而且,在外部/內部嵌套示例中,您需要將Python 3的* nonlocal *關鍵字寫入臨時範圍外的變量。 –

+0

@RaymondHettinger或者你可以將變量傳遞給函數。 –

+0

這對那些你想要寫入的非臨時變量有幫助嗎? OP的「大量使用這些功能的15行」大概是用這些縮寫功能來做的。 –

0

你可以在任何地方定義函數,調用它們,然後將其刪除。但是沒有辦法可以使用可以使用語句的匿名函數。

-3

一般來說,打字不寫軟件的最困難的部分,但如果你堅持:

class LocalNamespace(object): 
    def __enter__(self, *args, **kwargs): 
     pass 
    def __exit__(self, *args, **kwargs): 
     pass 
with LocalNamespace() as foo: 
    abspath = os.path.abspath 
    dirname = os.path.dirname 
    # etc. 

希望這有助於。

+0

在發佈之前,您是否測試了它? 'with'不會在Python中創建一個本地作用域;這些名字仍然可以在'with'塊之外找到。 – agf

+0

這沒有用。抱歉。 –

+0

是的,好吧。很公平。你是對的,在我發佈之前我沒有完全測試過。抱歉。 –

1

簡短答案是「否」。

Python有三個範圍。它具有函數範圍,全局(aka模塊)範圍和內置範圍。你可以聲明沒有其他範圍。

A class聲明看起來有點像一個範圍,但它不是。它基本上是在對象上分配一堆字段的簡寫。該類中的函數不通過它們定義的對象就不能訪問這些字段。

這聽起來有點比它更有限制。在Python中,你也可以嵌套函數定義。嵌套函數定義獲取對外部作用域的只讀訪問權限。這是'動態'。在定義函數之前,不必提及名稱。這裏有一個例子:

def joe(x): 
    def bar(): 
     return y 
    def baz(z): 
     y = x + 20 
     return x 
    y = x+5 
    return bar, baz 

>>> a, b = joe(5) 
>>> b(20) 
5 
>>> a() 
10 

所以,你可以排序的得到這個效果,而通過定義創建你所需要的值,使用它們,並返回結果嵌套函數犧牲太多地方。

我記得,在學習Python時,習慣了相當奇怪的範圍規則是其中一個比較困難的部分。當引入嵌套函數時,在我看來,由於外部範圍的只讀語義和閉包的動態範圍,他們使得範圍規則更加陌生。

顯然,在Python3有「的進口」從使用nonlocal關鍵字(類似於global關鍵字)封閉範圍的變量的方式,以便可以在一個讀/寫上下文使用它:

def joe(x): 
    def bar(): 
     return y 
    def baz(z): 
     nonlocal y 
     y = x + 20 
     return x 
    y = x+5 
    return bar, baz 

>>> a, b = joe(5) 
>>> b(20) 
5 
>>> a() 
25 

否則,只要Python在=符號的左側看到一個變量,就會假定您正在創建一個新的局部變量。關鍵字globalnonlocal是一種說明您打算修改不在該函數範圍內的變量的方式。

5

這種你想要做什麼,但你必須重複的名字

try: 
    abspath = os.path.abspath 
    dirname = os.path.dirname 
    # fifteen lines of code 
finally: 
    del abspath 
    del dirname 

這樣就避免了污染的命名空間,如果有一個情況如下

try: 
    ... 
    try: 
     abspath = os.path.abspath 
     dirname = os.path.dirname 
     # fifteen lines of code 
    finally: 
     del abspath 
     del dirname 

    ... # don't want abspath or dirname in scope here even if there was 
    ... # an exception in the above block 

except: 
    ... 
+0

+1 try/finally是一個很好的接觸:-) –

+0

@gnibbler我不確定要清楚地理解代碼的動機。我認爲這是使用'fuinally'子句總是執行的事實,它確保名稱abspath和dirname在它們的定義和指令中的含義之後被刪除。我對嗎 ?如果是這樣,我實際上並沒有看到爲什麼這個代碼會有相對於Raymond Hettinger代碼中的類似代碼片段的補充優點,只會刪除名稱以遵循定義和使用行。 – eyquem

+0

@eyquem,如果在同一範圍內有另一個try/expect塊,try/finally會確保變量不會泄漏,即使try/finally中存在異常 –

0

例外它不清楚,如果您更難以用寫入識別表達式的長度,或者使用後在名稱空間中留下剩餘標識符的痕跡。

  • 對於第一,定義別名長期前綴,如由雷蒙德赫廷傑描述的技術中,是採用的人。

  • 對於第二個問題,我很驚訝沒有人訴諸進口一個模塊,在這個模塊中,您認爲這些指令和線路被認爲是重物和亂拋垃圾。

順便說一句,如果你通過os.path.abspathos.path.dirnames訪問功能,這是不正確的說,功能(我想你的意思是他們的名字)垃圾的命名空間。由於它們屬於模塊osos。路徑(取決於哪一個已導入),只有模塊名稱'os''os.path'位於命名空間中,而模塊對象位於內存中,而不是函數的名稱直接在命名空間中。

因此,可以創建名爲 「heavy_code.py」 的腳本:

def doing(x): 
    from os.path import abspath as a,dirname as d 
    ## Execute all the desired processes and creations 
    def fufu(s,t): 
     return s+t 
    dedex = d[x] 
    #......... 
    #........... 
    #........ 
    #............ 
    #.......... 

    ## Return to the calling scope all that is needed there 
    return (dedex,fufu) 

和主模塊中,以gnibbler的回答帳戶:

one_path = 'I:/all/magala/zeru/kiol.py' 
try: 
    from pp.bududu import doing 
    w,ff = doing(one_path) 
finally: 
    del doing 

要看看它是如何工作的:在

in try : 
['__builtins__', '__doc__', '__name__', '__package__', 'doing', 'one_path'] 
executing doing() 
['__builtins__', '__doc__', '__name__', '__package__', 'doing', 'ff', 'one_path', 'w'] 

after finally : 
['__builtins__', '__doc__', '__name__', '__package__', 'ff', 'one_path', 'w'] 

w == I:/all/magala/zeru 
ff(10,12) == 22 

的片段執行後,該函數做()不存在了:

one_path = 'I:/all/magala/zeru/kiol.py' 
try: 
    from pp.bududu.heavy_code import doing 
    print "in try : " 
    print dir() 
    print "executing doing()" 
    w,ff = doing(one_path) 
    print dir() 
finally: 
    del doing 

print "\nafter finally : " 
print dir() 
print '\nw ==',w 
print 'ff(10,12) ==',ff(10,12) 

產生結果主模塊,而是由做執行()現在躺在它沒有名字的主模塊的名稱空間雜波創建的對象。此外,函數doing()內所需的所有標識符都是本地的。

所有需要的和需要的對象的創建可以委託給該模塊heavy_code,不管他們有多少,而進口和執行功能做()只需要兩行的主要模塊,並功能做()heavy_code加上其呼叫線可以很容易地修改。

是不是模塊設計用於什麼?

相關問題