1. 程式人生 > >Python學習:作用域(namespace)

Python學習:作用域(namespace)

python

Python作用域基礎

Python有四個作用域:

  1. L(Local)本地也稱作局部作用域;

  2. E(Enclosing)閉包函數外的函數中;

  3. G(global)全局作用域;

  4. B(Built-in)內建作用域;

變量可以在三個不同的地方分配:

  • 如果一個變量在def內賦值,它被定位在這個函數之內。

  • 如果一個變量在嵌套的def中賦值,對於嵌套的函數來說,它是非本地的。

  • 如果在def之外賦值,它就是整個文件全局的。

值得註意的是,修改一個對象並不是對一個名稱賦值。

變量名解析:LEGB原則

對於一個def語句:

變量名引用分為三個作用域進行查找:首先是本地,之後是函數內(如果有的話),之後全局,最後是內置。L->E->G->B

Python除了def/class/lambda外,其他如:if/elif/else/ try/except for/while並不能改變作用域。定義在他們之內的變量,外部還是可以訪問。

>>> if True:
...     a = ‘I am A‘
... 
>>> a
‘I am A‘
# 定義在if語言中的變量a,外部還是可以訪問的。
# 但是需要註意如果if被 def/class/lambda 包裹,在內部賦值,就變成了此 函數/類/lambda 的局部作用

def/class/lambda內進行賦值,就變成了其局部作用域。局部作用域會覆蓋全局作用域,但不會影響全局作用域。

g=1            #全局變量
def func():
    g = 2      #局部變量
    return g

print func()    #結果為2
print g         #結果為1

值得註意的是,有時候想再函數內調用全局變量,疏忽了會報錯,如下:

#file1
var = 1
def func():
    print var
    var = 200

func()

#file2
var = 1
def func():
    var = var +1
    return var

func()

#這兩個函數都會報錯UnboundLocalError: local variable ‘var‘ referenced before assignment

上述兩個函數都會報同樣的錯誤:為賦值之前引用變量!為什麽?在函數內部,解釋器探測到變量var重新被賦值,所以var變成了局部變量,但是在被賦值之前就使用了var,便會出現這個錯誤。解決的方法是在函數內部添加globals var語句,但運行函數後全局的var也會被修改。

#file1
var = 1
def func():
    global var
    print var
    var = 200

func()        #結果為1
print var     #全局變量var變為200

#file2
var = 1
def func():
    global var
    var = var +1
    return var

print func()        #結果為2


閉包Closure

閉包的定義:如果在一個內部函數裏,對外部函數內(不是全局變量)進行引用,那麽內部函數就被認為是閉包(closure)。

a = 1
def external():
    global a
    a = 200
    print a
    b =100
    def internal():
        print b
        b = 200
        return b
    internal()
    print b

print external()
#一樣會報錯,賦值前引用UnboundLocalError: local variable ‘b‘ referenced before assignment

Python3中有關鍵字nonlocal可以解決這個問題,但在Python2中盡量不要嘗試修改閉包中的變量。

關於閉包,還有一個坑:

from functools import wraps

def wrapper(log):
    def external(F):
        @wraps(F)
        def internal(**kw):
            if False:
                log = ‘modified‘
            print log
        return internal
    return external

@wrapper(‘first‘)
def abc():
    pass

print abc()

也會出現 引用在賦值之前 的錯誤,原因是解釋器探測到了 if False 中的重新賦值,所以不會去閉包的外部函數(Enclosing)中找變量,但 if Flase 不成立沒有執行,所以便會出現此錯誤。除非你還需要else: log=‘var‘ 或者 if True 但這樣添加邏輯語句就沒了意義,所以盡量不要修改閉包中的變量。

好像用閉包無法實現計數器功能,因為在閉包內部count+=1就會出現在賦值前引用的錯誤(Python3用關鍵字nonlocal可以解決)

def counter(start):
    count = [start]
    def internal():
        count[0] += 1
        return count[0]
    return internal

count = counter(0)
for n in range(10):
    print count()
#結果分別為1,2,3,4,5,6,7,8,9,10

count = counter(0)
print count()
#結果為1


global和globals()

global用來在函數內部聲明全局變量,globals()locals() 提供了基於字典的訪問全局和局部變量的方式。

比如:如果函數1內需要定義一個局部變量,名字另一個函數2相同,但又要在函數1內引用這個函數2。

def var():
    pass

def f2():
    var = ‘Just a String‘
    f1 = globals()[‘var‘]
    print var
    return type(f1)

print f2()
# Just a String
# <type ‘function‘>


Python學習:作用域(namespace)