1. 程式人生 > >一文搞懂Python函式(匿名函式、巢狀函式、閉包、裝飾器)!

一文搞懂Python函式(匿名函式、巢狀函式、閉包、裝飾器)!

## Python函式定義、匿名函式、巢狀函式、閉包、裝飾器 [TOC] #### 函式核心理解 > - 函式也是物件,可以把函式賦予變數 > - 可以把函式當作引數,傳入另一個函式中 > - 可以在函式裡定義函式,函式巢狀 > - 函式的返回值也可以是函式物件,閉包 #### 1. 函式定義 ```python def name(param1, param2, ..., paramN): statements return/yield value # optional ``` - def是可執行語句,函式直到被呼叫前,都是不存在的,當程式呼叫函式時,def語句才會建立一個新的函式物件,並賦予其名字 - 主程式呼叫函式時,必須保證這個函式此前已經定義過,不然會報錯 - 在函式內部呼叫其他函式時,函式間哪個申明在前、哪個在後無所謂,只要保證呼叫時,所需的函式都已經宣告定義 - python不用考慮輸入的資料型別,而是將其交給具體的程式碼去判斷執行,這種行為稱為**多型。** - python函式的引數可以設定預設值,可以指定資料型別 ```python def name(param1 = 0, param2: int, ..., paramN): ``` **函式作用** - 減少程式碼的重複性 - 模組化程式碼 #### 2. 巢狀函式 ##### 2.1 作用 - 能夠保證內部函式的隱私,內部的函式只能被外部函式所呼叫和訪問,不會暴露在全域性作用域 - 合理使用,可以提高程式的執行效率 ##### 2.2 函式變數作用域 - 區域性變數:函式內部定義的,只在函式內部有效,**一旦執行完畢,區域性變數就會被回收,無法訪問** - 全域性變數定義在整個層次上的,不能在函式內部隨意修改全域性變數的值,如要修改,加**global**關鍵字 - 函式內部,區域性變數和全域性變數同名,區域性變數會覆蓋全域性變數 - 巢狀函式,內部函式可以訪問外部函式定義的變數,但無法修改,如要修改,加**nonlocal**關鍵字 - 內部函式的變數和外部函式變數同名,覆蓋 ```python MIN = 1 # 全域性變數 MAX = 8 print(f"MIN={MIN},MAX={MAX}") def f1(): global MIN # 使用global關鍵字 修改全域性變數 MIN += 1 a = 5 # 區域性變數 b = 1 print(f"MIN={MIN},a={a},b={b}") def f2(): MAX = 9 # 區域性變數與全域性變數同名,區域性變數會覆蓋全域性變數 nonlocal a # 使用nonlocal關鍵字 修改外部函式的變數 a += 1 b = 2 # 內部函式變數與外部函式變數同名,覆蓋 print(f"MIN={MIN},MAX={MAX},a={a},b={b}") f2() # 內部函式被外部函式呼叫 f1() print(f"MIN={MIN},MAX={MAX}") ``` ```python MIN=1,MAX=8 MIN=2,a=5,b=1 MIN=2,MAX=9,a=6,b=2 MIN=2,MAX=8 ``` #### 3. 閉包 ##### 3.1 特點 - 和巢狀函式類似,只是,外部函式返回的是一個函式,而不是一個具體的值 - 返回的函式通常賦予一個變數,這個變數可以在後面被繼續執行呼叫 - 可以簡化程式的複雜度,提高可讀性 ##### 3.2 例項 ```python # 計算一個數的n次冪 def nth_power(exponent): def exponent_of(base): return base **exponent return exponent_of # 返回一個函式 # 呼叫函式 square = nth_power(2) print(square) print(square(3)) ``` ```python .exponent_of at 0x7f3120911b90> 9 ``` #### 4. 裝飾器 ##### 4.1 形式和作用 - @裝飾器函式,等價於,原函式名=裝飾器函式(原函式名) - 裝飾器就是通過裝飾器函式,來修改原函式的一些功能,使得原函式不需要修改,也就是擴充套件了原函式的功能 ##### 4.2 裝飾器函式寫法 ```python # 簡單裝飾器 def my_decorator(func): def wrapper(): print('wrapper of decorator') func() return wrapper # 帶引數的裝飾器 def my_decorator(func): def wrapper(message): print('wrapper of decorator') func(message) return wrapper # 通用的帶引數的裝飾器 def my_decorator(func): def wrapper(*args, **kwargs): print('wrapper of decorator') func(*args, **kwargs) return wrapper # 保留原函式的元資訊,將原函式的元資訊,拷貝到對應的裝飾器函式裡 import functools def my_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print('wrapper of decorator') func(*args, **kwargs) return wrapper @my_decorator def greet(message): print(message) greet('hello world') # 輸出 wrapper of decorator hello world # 類裝飾器,依賴函式__call__() class Count: def __init__(self, func): self.func = func self.num_calls = 0 def __call__(self, *args, **kwargs): self.num_calls += 1 print('num of calls is: {}'.format(self.num_calls)) return self.func(*args, **kwargs) @Count def example(): print("hello world") example() # 輸出 num of calls is: 1 hello world ``` ##### 4.3 裝飾器用法例項 - 身份認證、日誌記錄 - 測試某些函式的執行時間 ```python import time import functools def log_execution_time(func): @functools.wraps(func) def wrapper(*args, **kwargs): start = time.perf_counter() res = func(*args, **kwargs) end = time.perf_counter() print(f"{func.__name__} took {(end - start) * 1000)} ms") return res return wrapper @log_execution_time def calculate_similarity(items): ... ``` - 快取 python中內建的LRU cache,@lru_cache,會快取程序中的函式引數和結果,快取滿了之後,會刪除訪問時間最早的資料 - 工作中,二次開發,在原來的需求基礎上做優化,原邏輯不需要修改的化,只需增加新的業務場景的時候 #### 5. 匿名函式 ##### 5.1 格式 ```python lambda argument1, argument2,... argumentN : expression ``` - 此表示式返回的是一個函式物件,用法舉例 ```python # 計算一個數的平方 square = lambda x: x**2 # 返回一個函式物件 a = square(3) # 呼叫函式 print(a) # 9 ``` ##### 5.2 使用原則 - lambda是一個表示式,不是一個語句,只能寫成一行 - 程式中需要使用一個函式完成一個簡單的共嗯那個,並且該函式只調用一次 ##### 5.3 使用方式 - **1.用在列表內部** ```python # 計算列表0-9的數的平方 li = [(lambda x: x*x)(x) for x in range(10)] print(li) ``` ``` [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] ``` - **2.用作某些函式的引數** ```python # 按列表中元組的第1個元素排序 lis = [(1, 20), (3, 0), (9, 10), (2, -1)] lis.sort(key=lambda x: x[0]) print(lis) ``` ``` [(1, 20), (2, -1), (3, 0), (9, 10)] ``` ```python # 對一個字典,根據值進行由高到低的排序 d = {"mike": 10, "luck": 2, "ben": 30} new_li = sorted(d.items(), key=lambda x: x[1], reverse=True) # 返回的是列表巢狀元組型別 new_d = dict(new_li) print(d,'\n', new_li, '\n', new_d, sep='') ``` ```python {'mike': 10, 'luck': 2, 'ben': 30} [('ben', 30), ('mike', 10), ('luck', 2)] {'ben': 30, 'mike': 10, 'luck': 2} ``` - 3.資料清洗中,常用lambda函式 #### 6. python函數語言程式設計 ##### 6.1 概念 - 指程式碼中每一塊都是不可變的,都由純函式的形式組成 - 純函式,是指函式本身相互獨立、互不影響,對於相同的輸入,總會有相同的輸出 ##### 6.2函數語言程式設計的優缺點 - 優點主要在於其純函式和不可變的特性使程式更加健壯,易於除錯和測試 - 缺點主要在於限制多,難寫 ##### 6.3 map()、filter() 和 reduce()函式 - map(function, iterable),對序列中的每個元素都運用function這個函式,返回一個迭代器 ```python # 對列表中的每個元素乘以2 li = [1, 2, 3, 4, 5] new_list_1 = map(lambda x: x*2, li) print(list(new_list_1)) # 將迭代器轉換為列表 ``` ``` [2, 4, 6, 8, 10] ``` - filter(function, iterable),對序列中的每個元素,都使用function判斷,並返回True或者False,最後將返回**True的元素組成一個新的可遍歷的集合**,返回迭代器型別 ```python # 返回列表中能夠整除2的元素 li = [1, 2, 3, 4, 5] new_list_2 = filter(lambda x: x % 2 == 0, li) print(list(new_list_2)) ``` ``` [2, 4] ``` - reduce(function, iterable),規定有兩個引數,表示對序列中的每個元素以及上一次呼叫後的結果,運用function進行計算,最後返回的是一個單獨的數值 ```python # 計算列表元素的乘積 from functools import reduce li = [1, 2, 3, 4, 5] product = reduce(lambda x, y: x * y, li) print(product) ``` ```