能夠先看:http://www.cnblogs.com/youxin/p/3645734.htmlhtml
幾個概念:
python可以改變變量做用域的代碼段是def、class、lamda.
if/elif/else、try/except/finally、for/while 並不能涉及變量做用域的更改,也就是說他們的代碼塊中的變量,在外部也是能夠訪問的
變量搜索路徑是:本地變量->全局變量python
做用域搜索規則:web
LEGB Rule.閉包
L. Local. (Names assigned in any way within a function (def
or lambda
)), and not declared global in that function.app
E. Enclosing function locals. (Name in the local scope of any and all enclosing functions (def
or lambda
), form inner to outer.ide
「封閉式」的做用域規則適應於函數定義函數時,也就是說,在函數體內定義了一個新的函數。這個函數體內的函數是外函數的局部命名空間中的一部分,意味着只有在外函數執行期間纔可以運行函數
G. Global (module). Names assigned at the top-level of a module file, or declared global in a def
within the file.post
B. Built-in (Python). Names preassigned in the built-in names module : open
,range
,SyntaxError
,ui
下面一個程序,運行時爲何報錯:this
def func1(param=None): def func2(): if not param: param = 'default' print param # Just return func2. return func2 if __name__ == '__main__': func1('test')()
爲何?param是func1的。
先看下面的相似例子:Traceback (most recent call last): File "test.py", line 11, in func1('test')() File "test.py", line 3, in func2 if not param: UnboundLocalError: local variable 'param' referenced before assignment
def foo(): m=3 def bar(): a=4 return a+m return bar foo()()
運行正常,但是若是加上一句:
if not m: m=1
馬上報相似錯誤:UnboundLocalError: local variable 'm' referenced before assignment。
錯誤的緣由是python閉包原理;
def foo(): m=3 n=5 def bar(): a=4 return m+n+a return bar >>>bar = foo() >>>bar() 12
cell對象的引入,是爲了實現被多個做用域引用的變量。
對每個這樣的變量,都用一個cell對象來保存 其值 。
拿以前的示例來講,m和n既在foo函數的做用域中被引用,又在bar
函數的做用域中被引用,因此m, n引用的值,都會在一個cell對象中。
這兩個int型的cell分別存儲了m和n的值。
不管是在外部函數中定義,仍是在內部函數中調用,引用的指向都是cell對象中的值。
內部函數沒法修改cell對象中的值,若是嘗試修改m的值,編譯器會認爲m是函數
bar的局部變量,同時foo代碼塊中的m也會被認爲是函數foo的局部變量,兩個m分別在各自的做用域下起做用。
因此咱們看到了:
if not param:
param=5
嘗試給param賦值,param就成了局部變量,那麼以前的if not param就報錯,由於param尚未賦值。仍是不清楚爲何?參考:http://www.cnblogs.com/btchenguang/archive/2012/08/29/2662571.html
隔幾天有發現了答案:
http://www.cnblogs.com/youxin/p/3383059.html
你也許正疑惑爲何咱們的計數器是 的一個屬性而不是一個普通的變量。難道 的閉包環境不是讓咱們訪問在其局部做用域中聲明的任意變量麼?是的,但有個問題。def logging_decorator(func): def wrapper(): wrapper.count += 1 print "The function I modify has been called {0} time(s)".format(wrapper.count) func() wrapper.count = 0
return wrapper def a_function(): print "I'm a normal function."wrapperwrapper
但有個問題。Python中,閉包容許對其函數做用域鏈中任一變量的進行任意讀操做,但只容許對可變對象(列表、字典、等等)進行寫操做。整數在Python中是非可變對象,所以咱們不能修改 wrapper
內部整型變量的值。相反,咱們將計數器做爲 wrapper
的一個屬性—一個可變對象,所以能夠隨咱們本身增大它的值。。
def func1(): param=[1,2] def func2(): if not param: param.append(5) print param #just return func2 return func2 func1()();
程序運行正常。
若是改爲:
if not param:
param=[3,4]
仍是報一樣的錯。
因此,閉包對外部的可變變量不能使用=賦值,能夠修改。
If you’re curious, you can read about the principles of LEGB. You have to understand a bit about compilers and the AST to get what’s going on behind the scenes. You might think that replacing lines 3-4 with:
param = param or 'default'
Might work. But no. You can’t assign the same parameter at the local level if the enclosing level defines it. Even this fails:
param = param
What to do?
There are a few ways to get around this.
param
outside of func2. This doesn’t work if you need the default value to be dependent on what params func2 receives.param2
inside of func2 (posted below).Here is the solution suggested by our commenter Avazu:
def func1(param=None): def func2(param2=param): if not param2: param2 = 'default' print param2 # Just return func2. return func2
上面例子參考:http://blog.mozilla.org/webdev/2011/01/31/python-scoping-understanding-legb/