python 函數進階
函數進階
命名空間
namespace, 顧名思義, 就是存放名字的地方.舉例:若聲明變量 x = 1, 值1存放與內存中, 那變量名x 就存放在命名空間裏. 命名空間是存放x 和 1 綁定關系的地方.
- 名稱空間共3種,分別如下:
- locals: 當前所在的函數內的名稱空間,包括局部變量和形參
- globals: 全局變量,函數定義所在模塊最外層的名字空間
builtins: 內置模塊的名字空間
- 作用域範圍
- 全局範圍:全局存活,全局有效
局部範圍:臨時存活,局部有效
- LEGB 代表名字查找順序: locals -> enclosing function -> globals -> builtins
- locals 是函數內的名字空間,包括局部變量和形參
- enclosing 外部嵌套函數的名字空間
- globals 全局變量,函數定義所在模塊的名字空間
- builtins 內置模塊的名字空間
閉包
初步理解: 函數定義和函數表達式位於另外一個函數體內(嵌套函數),外部函數返回的是內部函數的函數名,而且內部 函數可以直接訪問他們所在的外部函數中聲明的所有局部變量、參數, 當這樣的一個內部函數在包含他們的外部函數之外被調用的時候,就會形成閉包。
定義: 返回的函數對象,不僅僅是一個函數對象,在該函數外還包裹了一層作用域,這使得,該函數無論在何處調用,除了函數內部定義的局部變量,其次就是使用自己外層包裹的作用域,
def outer():
name = ‘jack‘
def inner():
print("在inner裏打印外層函數的變量", name)
return inner
f = outer() # outer函數執行後等價於 f == inner
f() # inner()
裝飾器
- 作用: 在不改變現有函數的代碼的情況下,進一步增強函數的功能
- 前提條件: 能形成閉包, 函數作為參數傳入上級函數的命名空間,供內部函數返回掉調用
使用裝飾器版本
- 簡單實例:
def f1(func):
def inner():
return func(n)
return inner
@f1
def f2(x):
print(x)
f2()
過程詳細解析
- 給函數f2加上裝飾器f2可以理解為f = f1(f2)這個過程,此時先執行等式右邊內容,f2函數作為參數被傳入外部函數的命名空間,存儲起來;
- f1返回inner, 等價於f = inner;
- 這就形成了閉包,在inner函數外部函數f1的外部調用f()這個函數,就同時於調用了內部函數inner()
- 此時inner結果返回之前傳入的參數函數(),即f2()
- 此時inner函數就會向外部函數的命名空間查詢變量或參數,找到之前傳入的f2函數參數
- 由於之前傳入的函數名就是外部函數f2(),所以直接會調用外部函數f2()
- 最後,函數名修改後並不會影響函數內部代碼執行,所以直接將f 改為 f2, 那麽就是實現了裝飾器功能, 在不改變f2代碼的同時,在啟動前會運行f1(),此時生成中間函數明變量,函數名如果是f2的話,那麽在後續調用f2()的時候同時也就啟動了函數f2()。
實例2:給代碼加認證函數
USERNAME = ‘lynnfang‘
PASSWORD = ‘abc‘
lock_status = 0
login_status = 0
def login(func, *args):
def inner(*args, **kwargs):
global lock_status, login_status
if lock_status == 1:
print(‘您的賬戶已被鎖定!‘)
else:
if login_status == 0:
n = 0
while n < 3:
username = input(‘>>> 用戶名: ‘)
password = input(">>> 密碼: ")
if username == USERNAME and password == PASSWORD:
n = 3
login_status = 1
return func()
else:
n += 1
else:
lock_status = 1
else:
return func()
return inner
@login
def china():
print(‘----中國專區----‘)
@login
def japan():
print(‘----日本專區----‘)
china()
japan()
代碼解析
- 在不改變已寫好china()和japan()函數代碼的前提下,對其加用戶認證,只有認證通過才能調用函數內部表達式
- 首先將執行代碼腳本,python檢測到 @ 符號,將會將對應的函數名稱作為參數傳入login()函數的命名空間
- f1 = inner, f2 = inner
- 由於無形的中間函數可以賦值為之前傳入的函數名,所以當調用這個函數名時候等同於外部調用inner
- 進入inner函數後,聲明全局變量後,就可以在嵌套函數內部修改全局變量值,同時,在嵌套函數內部也會將全局變量值作為邏輯語句的判斷條件,但最後肯定會返回之前傳入的函數調用()
- 進入login()函數的命名空間查找對應的函數參數,由於函數參數就是之前傳入的外部函數,所以也就調用了外部函數,順利進入外部執行函數內部
- 只要不退出程序,下一次進入login函數就會參考之前的全局變量來判斷執行了,所以只要一次登錄,以後就不需要重復登錄了
生成器(generator)
特點: 生成器不會把結果保存在一個系列中,而是保持生成器的狀態,在每次進行叠代時返回一個值,直到遇到StopIteration異常結束。
生成器表達式: 通列表解析語法,只不過把列表解析的[ ]換成( )
與列表解析的差異:生成器表達式能做的事情列表解析基本都能處理,只不過在需要處理的序列比較大時,列表解析比較費內存。
典型生成器
g = (i for i in range(1000)
,g就是生成器,next(g)就可以叠代出內部的值,直到清空整個生成器range()在底層就是用生成器實現的, 在Python2裏,range(10)生產的就是列表,所以要用xrange(10),比較節約空間,python3優化了range(),結果是類生成器,
range(121212121212212)‘在python shell下可以馬上生成,但是
list(range(121212121212121212))`可能會很慢甚至直接出現內存錯誤,因為列表需要將所有的值都讀進內存,但是內存分配給列表的標準內存是有限的空間,所以會出現內存錯誤,此時生成器的有點就顯現出來了生成器是一次性的,叠代完成會出現
StopIteration
錯誤
yield理解
- yield的作用類似與return;
- 當python識別到next()函數,就會開始進入函數內部執行,直到遇到yield關鍵字,然後向外部返回yield對應的值;
- 之後的每次調用都會從yield關鍵字開始向後執行代碼,隨後從函數頭向下執行直到遇到yield返回值後就停止等待,直到遇到下一個next();
- g.send(’a‘)可以將’a‘賦值給yield
- 生成器實現range( )函數
def range2(end):
count = 0
while count < end:
n = yield count
count += 1
g = range2(10)
print(next(g))
print(next(g))
print(next(g))
g.send(‘stop‘)
- 將函數變成生成器,制作fabnacci序列生成器
def fab(max_num):
n, a, b = 0, 0, 1
while n < max_num:
yield b # 把函數執行過程凍結在這一步,並把b的值返回給外面的next(函數)
a, b = b, a+b
n += 1
g = fab(20)
print(g)
叠代器
可叠代對象(iterable):凡是可以用for循環的對象都是可叠代對象, 常見的數據集合list, tuple, set, dict, str都是可叠代對象
- 叠代器(iterator):
- 可以被next()調用並不斷返回下一個值的對象就是叠代器;
- 表示的是一個數據流,一個有序序列
- 惰性,只有在需要返回下一個數據時才會計算
無法預知大小,沒有len()這個方法
可叠代對象可以轉換成叠代器, 直接調用iter()函數
判斷可叠代對象:
isinstance(11, Iterable)
判斷是否是叠代器:
isinstance((i for i in range(10)), Iterator)
python 函數進階