1. 程式人生 > >python 函式作用域

python 函式作用域

變數可以在三個不同的地方分配
1. 如果一個變數在def內賦值,它被定位在這個函式之內
2. 如果一個變數在一個巢狀的def中賦值,對於巢狀函式來說,他是非本地的
3. 如果在def之外賦值,他就是整個檔案全域性的

作用域法則
1. 內嵌模組是全域性作用域
2. 全域性作用域的作用範圍僅限於單個檔案
3. 每次對函式的呼叫都建立了一個新的本地作用域
4. 函式中賦值的變數名除非宣告為全域性變數或非本地變數,否則均為本地變數
5. 所有其他的變數名都可以歸納為本地,全域性或者內建的

函式內部任何賦值都會把一個名稱劃分為本地的,包括:=語句,import中的模組名稱,def中的函式名稱,函式引數名稱等
函式內部改變物件並不會把變數劃分為本地,如果修改全域性列表L,L.append('a')

變數名解析
1. 變數名引用分為三個作用域進行查詢,首先是本地,然後是上層函式的本地作用域,之後全域性,最後是內建
2. 預設情況下,變數名賦值會建立或者改變本地變數
3. 全域性宣告和非本地宣告將賦值的變數名對映到模組檔案內部的作用域

內建作用域

內建作用域是一個名為builtin的內建模組,但是必須要import之後才能使用內建作用域,legb法則python會自動搜尋該模組,因此不需要匯入也可以使用內建變數

global語句告訴python函式打算生成一個或多個全域性變數名
全域性變數位於模組檔案內部頂層的變數名
全域性變數如果是在函式內被賦值的話,必須經過宣告
全域性變數在函式的內部不經過宣告也可以被引用

x=88
def func():
    global x    #宣告x引用def之外的x
    x=99
func()
print(x)    #99

x,y=1,2
def all_global():
    global x    #宣告x引用def之外的x,y也是全域性變數
    x=y+2
all_global()
print(x)    #4

#巢狀函式,x的作用域為當前函式和所有巢狀函式
x=99
def f1():
    x=88
    def f2():
        print(x)
        def f3():
            print(x)
        f3()
    f2()
    
f1()

#工廠函式,一個能夠記住巢狀作用域的變數值的函式,儘管那個作用域或許已經不存在
def f3():
    x=88
    def f4():
        print(x)
    return f4

action=f3()
action()  #結果:88,f4只存在與f3中,此時f4已經不處於啟用狀態,但是依舊記住了x的值

def maker(n):
    def action(x):
        return x**n
    return action

f=maker(2)
print(f(3))   #函式action記住了巢狀作用域變數n,因此3**2=9
g=maker(3)
print(g(3))    #得到一個新的n

#使用預設引數來保留巢狀作用域的狀態


def f5():
    x=88
    def f6(y=x):   #預設引數記住了f5中的x值
        print(y)
    return f6

f=f5()
f()

#避免使用def巢狀,等價函式如下
def f7():
    x=88
    f8(x)
    
def f8(x):
    print(x)
    
f7()

#巢狀作用域和lambda
def func():
    x=4
    action=(lambda n:x**n)   #lambda可使用巢狀函式變數
    return action

action=func()
print(action(2))

def func2():
    x=4
    action=(lambda n, m=x:m**n)   #lambda中使用預設函式來記住巢狀函式變數
    return action

action=func2()
print(action(2))

#作用域與帶有迴圈變數的預設引數相比較
#巢狀函式,巢狀在一個迴圈之中,並且巢狀函式引用上層作用域的變數,該變數被迴圈改變,所有在這個迴圈中產生的函式將會有相同的值,在最後一次迴圈中完成時被引用變數的值

def makeActions():
    acts=[]
    for i in range(5):
        acts.append(lambda x:i**x)
    return acts

acts=makeActions()
print(acts[0](2))   #i=4  4**2=16  
print(acts[1](2))   #i=4  4**2=16
print(acts[2](2))   #i=4  4**2=16
print(acts[3](2))   #i=4  4**2=16
print(acts[4](2))   #i=4  4**2=16

#如上程式,因為巢狀函式的作用域在被呼叫時才進行查詢,因此i=4,所以實際上記住的都是同樣的i,必須使用預設值引數傳遞
def makeActions():
    acts=[]
    for i in range(5):
        acts.append(lambda x, i=i:i**x)
    return acts

acts=makeActions()
print(acts[0](2))   #i=0  0**2=0
print(acts[1](2))   #i=1  1**2=1
print(acts[2](2))   #i=2  2**2=4
print(acts[3](2))   #i=3  3**2=9
print(acts[4](2))   #i=4  4**2=16

#nonlocal宣告將要在一個巢狀作用域中修改的名稱,使用nonlocal語句,巢狀的def可以對巢狀函式中的名稱進行讀取和寫入訪問
#nonlocal和global的不同之處在於,nonlocal應用於一個巢狀的函式的作用域中的一個名稱,而不是所有def之外的全域性模組作用域
#nonlocal申明名稱時,他必須已經存在與該巢狀函式的作用域中——他們可能只存在於一個巢狀的函式中,並且不能由一個巢狀的def中的第一次賦值建立
#換句話說,nonlocal即允許對巢狀的函式作用域中的名稱賦值,並且把這樣的名稱的作用域查詢限制在巢狀的def,而非本地作用域

def tester(start):
    state=start
    def nested(label):
        nonlocal state    #宣告state可以修改,如果不宣告,則不能修改state
        state+=1  
        print(label, state)

    return nested

f=tester(0)
f('spam')   #state=1,state+=1
f('eggs')   #state=2,state+=1

******************************************************************************************

def tester(start):
    global state
    state=start
    def nested(label):
        #nonlocal state    #宣告state可以修改,如果不宣告,則不能修改state
        global state       #此時如此使用global可以達到和nonlocal一樣的效果
        state+=1  
        print(label, state)

    return nested

f=tester(0)
f('spam')   #state=1,state+=1
f('eggs')   #state=2,state+=1