歸納總結Python函數進階的使用方法

2022-06-02 14:00:29
本篇文章給大家帶來了關於的相關知識,其中主要介紹了關於函數進階的使用方法,包括了函數的命名和作用域、函數的巢狀和作用域鏈、函數名的本質等等內容,下面一起來看一下,希望對大家有幫助。

推薦學習:

一、函數的名稱空間和作用域

1、函數的名稱空間

  • 什麼是名稱空間?

假如有一串程式碼,觀察其輸出結果:

def f():
    a = 1
    return a

print(a)

輸出結果:
Traceback (most recent call last):
  File "E:/python程式碼/11/檔案一.py", line 4, in <module>
    print(a)
NameError: name 'a' is not defined

報錯了!錯誤是「name 'a' is not defined」。變數a沒有被定義。。。為啥?我明明定義了a=1呀!

那我們就要了解一下Python程式碼執行時遇到函數時怎麼做的:

首先從python直譯器開始執行之後,就在記憶體中開闢了一個空間每當遇到一個變數的時候,就把變數名和值之間的對應關係記錄下來。但是當遇到函數定義的時候直譯器只是象徵性的將函數名讀入記憶體,表示知道這個函數的存在了,至於函數內部的變數和邏輯直譯器根本不關心。等執行到函數呼叫的時候,python直譯器會再開闢一塊記憶體來儲存這個函數裡的內容,這個時候,才關注函數裡面有哪些變數,而函數中的變數會儲存在新開闢出來的記憶體中。函數中的變數只能在函數的內部使用,並且會隨著函數執行完畢,這塊記憶體中的所有內容也會被清空。

我們給這個「存放名字與值的關係」的空間起了一個名字——叫做名稱空間

程式碼在最開始建立的儲存「變數名與值的關係」的空間叫做全域性名稱空間,在函數內部執行中開闢的臨時的空間叫做區域性名稱空間

  • 函數名稱空間三大類

函數名稱空間一共分為三大類

1、內建名稱空間 —— python直譯器
    # 就是python直譯器一啟動就可以使用的名字儲存在內建名稱空間中
    # 內建的名字在啟動直譯器的時候被載入進記憶體裡
2、全域性名稱空間 —— 我們寫的程式碼但不是函數中的程式碼
    # 是在程式從上到下被執行的過程中依次載入進記憶體的
    # 放置了我們設定的所有變數名和函數名
3、區域性名稱空間 —— 函數
    # 就是函數內部定義的名字
    # 當呼叫函數的時候 才會產生這個名稱空間 隨著函數執行的結束 這個名稱空間就又消失了

#在區域性:可以使用全域性、內建名稱空間中的名字
#在全域性:可以使用內建名稱空間中的名字,但是不能用區域性中使用
#在內建:不能使用區域性和全域性的名字的

內建名稱空間:內建名稱空間中存放了Python直譯器為我們提供的名字(函數)我們不需要定義,都是我們熟悉的開啟直譯器就能夠直接使用如:input、print、str、set……

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

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

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

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

例子:

a = 10
def f():
    a = 1
    print(a)
f()
print(a)

輸出結果:
1
10

2、函數的作用域

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

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

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

  • globals和locals方法

locals(): 函數會以字典型別返回當前位置的全部區域性變數
globals(): 函數會以字典型別返回當前位置的全部全域性變數

def func():
    a = 1
    print(locals())
    print(globals())
    print('========================分割線==========================')
func()
print(locals())
print(globals())

輸出結果:

  • global關鍵字

1、global是Python中的全域性變數關鍵字。
2、變數分為區域性變數與全域性變數,區域性變數又可稱之為內部變數。
3、由某物件或某個函數所建立的變數通常都是區域性變數,只能被內部參照,而無法被其它物件或函數參照。
4、全域性變數既可以是某物件函數建立,也可以是在本程式任何地方建立。全域性變數是可以被本程式所有物件或函數參照。
5、global關鍵字的作用是可以使得一個區域性變數為全域性變數

例子:

在my函數中,在 x 前面加 global,my函數將 x 賦為8,此時全域性變數中的 x 值改變。需要注意的是 global 需要在函數內部宣告,若在函數外宣告,則函數依然無法操作 x 。

x = 4
 
def my():
    global x
    x = 8
    print("x = ", x)
 
print("x = ", x)
my()
print("x = ", x)
 
 
輸出結果是:
x = 4
x = 8
x = 8

二、函數的巢狀和作用域鏈

  • 函數的巢狀呼叫

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

ret = max4(1,2,4,3)
print(ret)

輸出結果:
4
  • 函數的巢狀定義

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

    f2()
f1()

輸出結果:
in f1
in f2

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

        print("in f2")
        f3()

    print("in f1")
    f2()


f1()

輸出結果:
in f1
in f2
in f3
  • 函數的作用域鏈

a = 1
def outer():
    a = 5
    def inner():
        a = 2
        def inner2():
            nonlocal a
            a += 1
            print('inner2',a)
        inner2()
        print('##a##:',a)
    inner()
    print('**a**:',a)

outer()

print('全域性:',a)

輸出結果:
inner2 3
##a##: 3
**a**: 5
全域性: 1
  • nonlocal關鍵字

#nonlocal 只能用於區域性變數,找上層中離當前函數最近一層的區域性變數且外部必須有這個變數
#宣告了nonlocal的內部函數的變數修改會影響到離當前函數最近一層的區域性變數
#對全域性無效,在內部函數宣告nonlocal變數之前不能再出現同名變數
#對區域性也只是對最近一層有影響
def f1():
    a = 1
    def f2():
        nonlocal a
        a = 2
    f2()
    print('a in f1 : ',a)

f1()

輸出結果:
a in f1 :  2
  • global關鍵字

# 對於不可變資料型別 在區域性可是檢視全域性作用域中的變數
# 但是不能直接修改
# 如果想要修改,需要在程式的一開始新增global宣告
# 如果在一個區域性(函數)內宣告了一個global變數,那麼這個變數在區域性的所有操作將對全域性的變數有效

三、函數名的本質

  1. 函數名就是記憶體地址

  2. 函數名可以被賦值

  3. 函數名可以作為容器型別的元素

  4. 函數名可以作為函數的返回值

  5. 函數名可以作為函數的引數

def func():
    print(123)
func()
print(func)
# 函數名就是記憶體地址
# 函數名可以被賦值
func2 = func
func2()

#函數名可以作為容器型別的元素
l = [func,func2]
for i in l:
    i()
def func():
    print(123)

def wahaha(f):
    f()
    return f            #函數名可以作為函數的返回值
qqxing = wahaha(func)   #函數名可以作為函數的引數
qqxing()

輸出結果:
123
<function func at 0x000001ADF9946280>
123
123
123
123
123
  • 思考

如果我自己定義了一個input函數(作用:呼叫該函數就列印'在下週周ovo'),會不會與內建的input函數有衝突呢?

def input(a):
    print('在下週周ovo')

那麼接下來的程式碼怎麼執行呢?

def input(a):
    print('在下週周ovo')

def func():
    input('請輸入')
    print(input)

func()

答案:


四、閉包

  • 閉包函數的概念

內部函數包含對外部作用域而非全劇作用域名字的參照,該內部函數稱為閉包函數
#函數內部定義的函數稱為內部函數

由於有了作用域的關係,我們就不能拿到函數內部的變數和函數了。如果我們就是想拿怎麼辦呢?返回呀!

如果函數內的變數我們要想在函數外部用,可以直接返回這個變數,那麼如果我們想在函數外部呼叫函數內部的函數呢?那就直接將函數名字作為返回值就好

def outer():
    a = 1
    def inner():
        print(a)    #內部函數呼叫了外部變數a
    return inner

inn = outer()
inn()

輸出結果:
1
  • 閉包函數的判斷方法

判斷閉包函數的方法__closure__

當執行後,如果有cell的話,就表示是閉包函數。如果沒有就不是。

#輸出的__closure__有cell元素 :是閉包函數
def func():
    name = 'eva'
    def inner():
        print(name)
    print(inner.__closure__)
    return inner

f = func()
f()

#輸出的__closure__為None :不是閉包函數
name = 'egon'
def func2():
    def inner():
        print(name)
    print(inner.__closure__)
    return inner

f2 = func2()
f2()

輸出結果:
(<cell at 0x000001E935CB0FA0: str object at 0x000001E935CC2CB0>,)
eva
None
egon
  • 閉包巢狀

顧名思義是兩個或以上的閉包函數巢狀在一起

def wrapper():
    money = 10
    def func():
        name = 'zhou'    
        def inner():
            print(name,money)    #參照了func()函數中name變數參照了wrapper()函數中money變數
        return inner
    return func

f = wrapper()
i = f()
i()

輸出結果:
zhuo 10

小結

#func(一個函數名)  --->>對應函數的記憶體地址
#函數名()---函數呼叫
#函數的記憶體地址----()函數的呼叫
# 作用域兩種
# 全域性作用域 —— 作用在全域性 —— 內建和全域性名稱空間中的名字都屬於全域性作用域  ——globals()
# 區域性作用域 —— 作用在區域性 —— 函數(區域性名稱空間中的名字屬於區域性作用域) ——#locals()globals() : 永遠列印全域性的名字
#locals()  : 輸出什麼 根據locals所在位置
#在程式碼中要儘量少定義全域性變數,多使用返回值和接收返回值
#函數的巢狀:
        巢狀呼叫
        巢狀定義:定義在內部的函數無法直接在全域性被呼叫
#函數名的本質:
        就是一個變數,儲存了函數所在的記憶體地址
#閉包:
        內部函數包含對外部作用域而非全劇作用域名字的參照,該內部函數稱為閉包函數

推薦學習:

以上就是歸納總結Python函數進階的使用方法的詳細內容,更多請關注TW511.COM其它相關文章!