1. 程式人生 > >day9 python之函式進階

day9 python之函式進階

python之函式進階

 

一,引言

現在我有個問題,函式裡面的變數,在函式外面能直接引用麼?

複製程式碼
def func1():
    m = 1
    print(m)

print(m)  #這行報的錯


報錯了:
NameError: name 'm' is not defined

 

複製程式碼

上面為什麼會報錯呢?現在我們來分析一下python內部的原理是怎麼樣:

  我們首先回憶一下Python程式碼執行的時候遇到函式是怎麼做的,從Python直譯器開始執行之後,就在記憶體中開闢裡一個空間,每當遇到一個變數的時候,就把變數名和值之間對應的關係記錄下來,但是當遇到函式定義的時候,直譯器只是象徵性的將函式名讀如記憶體,表示知道這個函式存在了,至於函式內部的變數和邏輯,直譯器根本不關心。

  等執行到函式呼叫的時候,Python直譯器會再開闢一塊記憶體來儲存這個函式裡面的內容,這個時候,才關注函式裡面有哪些變數,而函式中的變量回儲存在新開闢出來的記憶體中,函式中的變數只能在函式內部使用,並且會隨著函式執行完畢,這塊記憶體中的所有內容也會被清空。

我們給這個‘存放名字與值的關係’的空間起了一個名字-------名稱空間。

程式碼在執行伊始,建立的儲存“變數名與值的關係”的空間叫做全域性名稱空間;

在函式的執行中開闢的臨時的空間叫做區域性名稱空間。

 二,名稱空間和作用域

>>> import
this The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules.
Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those! python之禪
View Code

 


複製程式碼
>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

python之禪
複製程式碼

在python之禪中提到過:名稱空間是一種絕妙的理念,讓我們盡情的使用發揮吧!

名稱空間一共分為三種:

  全域性名稱空間

  區域性名稱空間

  內建名稱空間

*內建名稱空間中存放了python直譯器為我們提供的名字:input,print,str,list,tuple...它們都是我們熟悉的,拿過來就可以用的方法。

三種名稱空間之間的載入與取值順序:

載入順序:內建名稱空間(程式執行前載入)->全域性名稱空間(程式執行中:從上到下載入)->區域性名稱空間(程式執行中:呼叫時才載入)

取值順序:

  在區域性呼叫:區域性名稱空間->全域性名稱空間->內建名稱空間

  在全域性呼叫:全域性名稱空間->內建名稱空間

綜上所述,在找尋變數時,從小範圍,一層一層到大範圍去找尋。

作用域

作用域就是作用範圍,按照生效範圍可以分為全域性作用域和區域性作用域。

全域性作用域:包含內建名稱空間、全域性名稱空間,在整個檔案的任意位置都能被引用、全域性有效

區域性作用域:區域性名稱空間,只能在區域性範圍生效

globals和locals方法

print(globals())
print(locals())

 

def func():
    a = 12
    b = 20
    print(locals())
    print(globals())

func()

 

global關鍵字,nonlocal關鍵字。

global:

  1,宣告一個全域性變數。

  2,在區域性作用域想要對全域性作用域的全域性變數進行修改時,需要用到 global(限於字串,數字)。

def func():
    global a
    a = 3
func()
print(a)


count = 1
def search():
    global count
    count = 2
search()
print(count)

 

 ps:對可變資料型別(list,dict,set)可以直接引用不用通過global。

li = [1,2,3]
dic = {'a':'b'}

def change():
    li.append('a')
    dic['q'] = 'g'
    print(dic)
    print(li)
change()
print(li)
print(dic)

 

nonlocal:

  1,不能修改全域性變數。

  2,在區域性作用域中,對父級作用域(或者更外層作用域非全域性作用域)的變數進行引用和修改,並且引用的哪層,從那層及以下此變數全部發生改變。

def add_b():
    b = 42
    def do_global():
        b = 10
        print(b)
        def dd_nonlocal():
            nonlocal b
            b = b + 20
            print(b)
        dd_nonlocal()
        print(b)
    do_global()
    print(b)
add_b()

 

三,函式的巢狀和作用域鏈

函式的巢狀呼叫

def max2(x,y):
    m  = x if x>y else y
    return m

def max4(a,b,c,d):
    res1 = max2(a,b)
    res2 = max2(res1,c)
    res3 = max2(res2,d)
    return res3

# max4(23,-7,31,11)

 


複製程式碼
def max2(x,y):
    m  = x if x>y else y
    return m

def max4(a,b,c,d):
    res1 = max2(a,b)
    res2 = max2(res1,c)
    res3 = max2(res2,d)
    return res3

# max4(23,-7,31,11)

函式的巢狀呼叫
複製程式碼

函式的巢狀定義

def f1():
    print("in f1")
    def f2():
        print("in f2")

    f2()
f1()
###########
def f1():
    def f2():
        def f3():
            print("in f3")
        print("in f2")
        f3()
    print("in f1")
    f2()
    
f1()

 


複製程式碼
def f1():
    print("in f1")
    def f2():
        print("in f2")

    f2()
f1()
###########
def f1():
    def f2():
        def f3():
            print("in f3")
        print("in f2")
        f3()
    print("in f1")
    f2()
    
f1()
複製程式碼

函式的作用域鏈:小範圍作用域可以使用大範圍的變數,但是反之不行,他是單向的。

def f1():
    a = 1
    def f2():
        def f3():
            print(a)
        f3()
    f2()

f1()
################
def f1():
    a = 1
    def f2():
        a = 2
    f2()
    print('a in f1 : ',a)

f1()

 

一,引言

現在我有個問題,函式裡面的變數,在函式外面能直接引用麼?

複製程式碼
def func1():
    m = 1
    print(m)

print(m)  #這行報的錯


報錯了:
NameError: name 'm' is not defined

 

複製程式碼

上面為什麼會報錯呢?現在我們來分析一下python內部的原理是怎麼樣:

  我們首先回憶一下Python程式碼執行的時候遇到函式是怎麼做的,從Python直譯器開始執行之後,就在記憶體中開闢裡一個空間,每當遇到一個變數的時候,就把變數名和值之間對應的關係記錄下來,但是當遇到函式定義的時候,直譯器只是象徵性的將函式名讀如記憶體,表示知道這個函式存在了,至於函式內部的變數和邏輯,直譯器根本不關心。

  等執行到函式呼叫的時候,Python直譯器會再開闢一塊記憶體來儲存這個函式裡面的內容,這個時候,才關注函式裡面有哪些變數,而函式中的變量回儲存在新開闢出來的記憶體中,函式中的變數只能在函式內部使用,並且會隨著函式執行完畢,這塊記憶體中的所有內容也會被清空。

我們給這個‘存放名字與值的關係’的空間起了一個名字-------名稱空間。

程式碼在執行伊始,建立的儲存“變數名與值的關係”的空間叫做全域性名稱空間;

在函式的執行中開闢的臨時的空間叫做區域性名稱空間。

 二,名稱空間和作用域

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

python之禪
View Code

 


複製程式碼
>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

python之禪
複製程式碼

在python之禪中提到過:名稱空間是一種絕妙的理念,讓我們盡情的使用發揮吧!

名稱空間一共分為三種:

  全域性名稱空間

  區域性名稱空間

  內建名稱空間

*內建名稱空間中存放了python直譯器為我們提供的名字:input,print,str,list,tuple...它們都是我們熟悉的,拿過來就可以用的方法。

三種名稱空間之間的載入與取值順序:

載入順序:內建名稱空間(程式執行前載入)->全域性名稱空間(程式執行中:從上到下載入)->區域性名稱空間(程式執行中:呼叫時才載入)

取值順序:

  在區域性呼叫:區域性名稱空間->全域性名稱空間->內建名稱空間

  在全域性呼叫:全域性名稱空間->內建名稱空間

綜上所述,在找尋變數時,從小範圍,一層一層到大範圍去找尋。

作用域

作用域就是作用範圍,按照生效範圍可以分為全域性作用域和區域性作用域。

全域性作用域:包含內建名稱空間、全域性名稱空間,在整個檔案的任意位置都能被引用、全域性有效

區域性作用域:區域性名稱空間,只能在區域性範圍生效

globals和locals方法

print(globals())
print(locals())

 

def func():
    a = 12
    b = 20
    print(locals())
    print(globals())

func()

 

global關鍵字,nonlocal關鍵字。

global:

  1,宣告一個全域性變數。

  2,在區域性作用域想要對全域性作用域的全域性變數進行修改時,需要用到 global(限於字串,數字)。

def func():
    global a
    a = 3
func()
print(a)


count = 1
def search():
    global count
    count = 2
search()
print(count)

 

 ps:對可變資料型別(list,dict,set)可以直接引用不用通過global。

li = [1,2,3]
dic = {'a':'b'}

def change():
    li.append('a')
    dic['q'] = 'g'
    print(dic)
    print(li)
change()
print(li)
print(dic)

 

nonlocal:

  1,不能修改全域性變數。

  2,在區域性作用域中,對父級作用域(或者更外層作用域非全域性作用域)的變數進行引用和修改,並且引用的哪層,從那層及以下此變數全部發生改變。

def add_b():
    b = 42
    def do_global():
        b = 10
        print(b)
        def dd_nonlocal():
            nonlocal b
            b = b + 20
            print(b)
        dd_nonlocal()
        print(b)
    do_global()
    print(b)
add_b()

 

三,函式的巢狀和作用域鏈

函式的巢狀呼叫

def max2(x,y):
    m  = x if x>y else y
    return m

def max4(a,b,c,d):
    res1 = max2(a,b)
    res2 = max2(res1,c)
    res3 = max2(res2,d)
    return res3

# max4(23,-7,31,11)

 


複製程式碼
def max2(x,y):
    m  = x if x>y else y
    return m

def max4(a,b,c,d):
    res1 = max2(a,b)
    res2 = max2(res1,c)
    res3 = max2(res2,d)
    return res3

# max4(23,-7,31,11)

函式的巢狀呼叫
複製程式碼

函式的巢狀定義

def f1():
    print("in f1")
    def f2():
        print("in f2")

    f2()
f1()
###########
def f1():
    def f2():
        def f3():
            print("in f3")
        print("in f2")
        f3()
    print("in f1")
    f2()
    
f1()

 


複製程式碼
def f1():
    print("in f1")
    def f2():
        print("in f2")

    f2()
f1()
###########
def f1():
    def f2():
        def f3():
            print("in f3")
        print("in f2")
        f3()
    print("in f1")
    f2()
    
f1()
複製程式碼

函式的作用域鏈:小範圍作用域可以使用大範圍的變數,但是反之不行,他是單向的。

def f1():
    a = 1
    def f2():
        def f3():
            print(a)
        f3()
    f2()

f1()
################
def f1():
    a = 1
    def f2():
        a = 2
    f2()
    print('a in f1 : ',a)

f1()