首頁 > 軟體

Python基礎globlal nonlocal和閉包函數裝飾器語法糖

2022-11-05 14:00:53

一、global與nonlocal

1、global

  • 在py檔案中,一般無法呼叫函數體內變數名,而global可使函數體程式碼內的變數名直接在函數體外部呼叫,條件是在需要呼叫的程式碼體中使用global 呼叫需要的變數名

未使用global情況:

# 在外部繫結一個變數名
name = 'kangkng'
# 定義一個函數體程式碼
def func():
    # 函數體內部重新系結一個變數名
    name = 'zhangzhang'
# 呼叫函數func
func()
# 這時無法列印函數體內部的變數名
print(name)
------------------------------------------------------------------------ kangkang   

使用global情況:

# 在函數體內部使用global時,可在外部直接呼叫函數內部變數名
name = 'kangkng'
# 定義一個函數體程式碼
def func():
    # 使用global呼叫變數名
    global name
    # 函數體內部重新系結一個變數名
    name = 'zhangzhang'
# 呼叫函數func
func()
# 外py中列印name
print(name)
------------------------------------------------------------------------
zhangzhang

2、nonlocal

  • 在函數巢狀使用時,通常在父程式碼體中無法呼叫子程式碼中變數名,

而nonlocal的作用是,可以在父程式碼中直接呼叫子程式碼中的變數名,條件是需要在子程式碼中使用nonlocal 呼叫需要的變數名

未使用nonlocal情況:

# 定義一個函數體程式碼
def outer():
    # 繫結一個變數名
    name = 'kangkang'
    # 程式碼體內部再次定義一段函數體
    def subcoat():
        # 內層中繫結變數名
        name = 'zhangzhang'
    # 在函數外層列印變數名
    print(name)
# 呼叫外層函數體程式碼
outer()
----------------------------------------------------------------------- 
kangkang

使用nonlocal情況:

# 在函數體內部使用global時,可在外部直接呼叫函數內部變數名
def outer():
    # 函數外層繫結一個變數名
    name = 'kangkang'
    # 程式碼體內部再次定義一段函數體
    def subcoat():
        # 在函數體內層使用nonlocal,呼叫變數名
        nonlocal name
        # 內層中繫結變數名
        name = 'zhangzhang'
    # 呼叫內層函數
    subcoat()
    # 在函數外層列印變數名
    print(name)
# 呼叫外層函數體程式碼
outer()   
----------------------------------------------------------------------
zhangzhang

二、函數名的多種用法

引言:

​ 函數名就相當於變數名,只不過函數名系結的是一段函數體程式碼,在我們使用這個函數名加括號時就可以呼叫這段程式碼體,具體由以下幾種用法:

1、當做變數名賦值

def func():
    print('我是func函數體程式碼')
res = func
print(res())
------------------------------------------------------------------------
我是func函數體程式碼
None

2、當作函數的引數

def func():
    print('我是func函數體程式碼')
def func1(a):
    print('我是func1函數體程式碼', a)
    a()
func1(func)
------------------------------------------------------------------------
我是func1函數體程式碼 <function func at 0x000001D0C14D6310>
我是func函數體程式碼

3、當作函數的返回值

def func():
    print('我是func函數體程式碼')
def func1():
    print('我是func1函數體程式碼')
    return func
res = func1()
print(res)
res()
------------------------------------------------------------------------
我是func1函數體程式碼
<function func at 0x00000218F95B6310>
我是func函數體程式碼

4、當作容器型別的資料

def spring():
    print('我是春季,生機盎然')
def summer():
    print('我是夏季,活力四射')
def autumn():
    print('我是秋季,翩翩起舞')
def winter():
    print('我是冬季,大雪紛飛')
while True:
    season_dict = { '1': spring,
               '2': summer,
               '3': autumn,
               '4': winter
                   }
    season_select = input('根據編號,選擇您喜歡的季節>>>:').strip()
    if season_select in season_dict:
        season_dict.get(season_select)()
    else:
        print('你選擇的編號不存在')
------------------------------------------------------------------------

三、閉包函數

1、什麼是閉包函數

  一個函數的返回值是另外一個函數,返回的函數呼叫父函數內部的變數,如果返回的函數在外部被執行,就產生了閉包

2、閉包函數需滿足的條件

滿足以下兩個條件的就是閉包函數:

條件一:定義在函數內部的函數

條件二:用到了外部函數空間名稱中的名子

3、閉包函數的作用

作用:使函數外部能夠呼叫函數內部放入屬性和方法

缺點:閉包操作導致整個函數的內部環境被長久儲存,佔用大量記憶體

4、閉包函數的實際應用

1.函數內部變數名在外部被存取

def fun1():
    name = 'python'
    def inner():
        print(name)
    return inner
result = fun1()
result()
------------------------------------------------------------------------
python

2.函數體內部函數體程式碼可以通過外部存取

def fun2():
    def inner():
        print("執行了內部函數inner")
    def all():
        return inner
    return all
result = fun2()
result()()
------------------------------------------------------------------------
執行了內部函數inner

四、裝飾器

​ 當我們需要將一段函數體程式碼在不改變呼叫方式和原始碼的情況下,需要給這個段程式碼新增新的功能時,這時候我們就需要給這段程式碼安裝一個裝飾器,裝飾器是指將這段程式碼封裝在閉包函數內,來達到既能滿足上述條件,又能增加新的功能的條件

概念

  • 在不修改被裝飾物件原始碼和呼叫方式的情況下給被裝飾的物件新增新的功能

本質

  • 並不是一門新的技術,而是由函數引數、名稱空間、函數名多種用法、閉包函陣列合到一起的效果

口訣

  • 對修改封閉,對擴充套件開放

1、裝飾器推導流程

1、首先定義一段函數體程式碼,當我們給這段函數傳入指定的引數時,他就會暫停一秒,然後執行,使它在執行結束後,能夠統計它的執行時間

import time
def index(a, b):
    time.sleep(1)
    print(index,a, b)

2、通常,我們只需要在這段程式碼執行前列印一個時間戳,執行後再次列印一個時間戳,在這段程式碼執行結束後通過前後時間的插值就能統計出這段程式碼的執行時間,但這種辦法使用起來比較麻煩且只能使用一次

方法一:
    import time
    def index(a, b):
        start = time.time()
        time.sleep(1)
        print(index, a, b)
        end = time.time()
        print(end - start)
    index(1,2)
方式二:
    import time
    def index(a, b):
        time.sleep(1)
        print(index, a, b)
    start = time.time()
    index(1,2)
    end = time.time()
    print(end - start)

3、通過上述方法的方式二,我們可以得出將函數名包裹在統計時間功能程式碼內,這樣在呼叫時相對便捷,進一步思考,若將這段程式碼使用函數封包,那樣在呼叫時就可以更為便捷,在以後統計該程式碼時,只需要呼叫封包這段程式碼的函數名就可以直接統計這段程式碼的執行時間

import time
def index(a, b):
    time.sleep(1)
    print(index, a, b)
def time_():
    start = time.time()
    index()
    end = time.time()
    print(end - start)
time_()
------------------------------------------------------------------------
Traceback (most recent call last):
  File "D:/pytcharm專案檔案路徑/38/11.py", line 297, in <module>
    time_()
  File "D:/pytcharm專案檔案路徑/38/11.py", line 293, in time_
    index()
TypeError: index() missing 2 required positional arguments: 'a' and 'b'

4、雖然這種方式可以行得通,但只能針對沒有引數的函數體程式碼,若這段程式碼需要傳參者無法執行,並直接報錯。再次進一步思考,只需要將封包的這段函數設定為有參函數就可解決這個問題

import time
def index(a, b):
    time.sleep(1)
    print(index, a, b)
def core(a,b):
    start = time.time()
    index(a, b)
    end = time.time()
    print(end - start)
core(1, 2)
------------------------------------------------------------------------
<function index at 0x000001F4A0026310> 1 2
1.0047826766967773

5、由上推導可看出,雖然此功能可以更為便捷的統計程式碼執行時間,但若是原始碼的引數需要修改則封包它的引數也需要修改,這時我們可聯想到將引數修改為可變長引數,就不會出現這個問題

import time
def index(a, b):
    time.sleep(1)
    print(index, a, b)
def core(*args,**kwargs):
    start = time.time()
    index(*args, **kwargs)
    end = time.time()
    print(end - start)
core(1,2)
------------------------------------------------------------------------
<function index at 0x000002ECDD4E6310> 1 2
1.004744529724121

6、這樣無論原始碼引數如何修改,我們都可以進行傳參,雖然這個問題解決了,但考慮使用的廣泛性,若有其他函數體也需要用到這個功能時,還需要重新修改封包內程式碼,這時,我們可以使用閉包的方式來滿足這個條件

import time
def index(a, b):
    time.sleep(1)
    print(index, a, b)
def func(x, y, z):
    time.sleep(2)
    print(func, x, y, z)
def outer(index):
    def core(*args, **kwargs):
        start = time.time()
        index(*args, **kwargs)
        end = time.time()
        print(end - start)
    return core
res = outer(func)
res(1, 2, 3)
------------------------------------------------------------------------
<function func at 0x0000018C23686670> 1 2 3
2.00856614112854

7、通過將原始碼函數名放至閉包函數引數內,就可以達到可以調動任何函數體程式碼都可以執行此功能的方法,但並未滿足閉包函數的條件,原始碼的呼叫方式改變了,這時我們可以通過將原函數體程式碼賦值的方式來達到呼叫方式和原始碼都未改變的情況下來增加此功能

import time
def index(a, b):
    time.sleep(1)
    print(index, a, b)
def func(x, y, z):
    time.sleep(2)
    print(func, x, y, z)
def outer(index):
    def core(*args, **kwargs):
        start = time.time()
        index(*args, **kwargs)
        end = time.time()
        print(end - start)
    return core
index = outer(index)
index(1,2)
func = outer(func)
func(1, 2, 3)
------------------------------------------------------------------------
<function outer.<locals>.core at 0x0000026C17F58280> 1 2
1.004807710647583
<function outer.<locals>.core at 0x0000026C17F58940> 1 2 3
2.0077626705169678

8、雖然上述推導過程都已滿足裝飾器條件,但是考慮到原始碼有返回值的情況,我們沒有並沒有獲取,這時在進一步推導,可在裝飾器函數內部呼叫原始碼函數名的位置設定一個變數名用於接收返回值,傳給裝飾器底層return用於接收即可解決這個問題

import time
def index(a, b):
    time.sleep(1)
    print(index, a, b)
    return 'index'
def func(x, y, z):
    time.sleep(2)
    print(func, x, y, z)
    return 'func'
def outer(index):
    def core(*args, **kwargs):
        start = time.time()
        res = index(*args, **kwargs)
        end = time.time()
        print(end - start)
        return res
    return core
index = outer(index)
res = index(1,2)
print(res)
func = outer(func)
res = func(1, 2, 3)
print(res)
------------------------------------------------------------------------
<function outer.<locals>.core at 0x0000020C50A78280> 1 2
1.0050580501556396
index
<function outer.<locals>.core at 0x0000020C50A78940> 1 2 3
2.0094454288482666
func

2、裝飾器語法糖

什麼是裝飾器語法糖

當我們使用裝飾器呼叫被裝飾的函數體程式碼時,總是需要在呼叫前通過賦值的方式來呼叫,這樣的方式相對比較麻煩,這時我們就可以用到裝飾器語法糖來節省時間和程式碼

語法糖的使用方法和條件

用法:在原始碼函數體上方使用@加裝飾器函數名

條件:原始碼需在裝飾器下方

具體用法

import time
def outer(index):
    def core(*args, **kwargs):
        start = time.time()
        res = index(*args, **kwargs)
        end = time.time()
        print(end - start)
        return res
    return core
@outer
def index(a, b):
    time.sleep(1)
    print(index, a, b)
    return 'index'
index(1,2)

3、裝飾器模板

def outer(func):
    def inner(*args, **kwargs):
        # 執行被裝飾物件之前可以做的額外操作
        res = func(*args, **kwargs)
        # 執行被裝飾物件之後可以做的額外操作
        return res
    return inner

以上就是Python基礎globlal nonlocal和閉包函數裝飾器語法糖的詳細內容,更多關於Python基礎globlal nonlocal的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com