函式物件,函式的巢狀,名稱空間與作用域,閉包函式
阿新 • • 發佈:2020-12-29
一、函式物件
在python中,函式是第一類物件,函式是第一等公民
本質:函式可以當變數用
1、可以賦值,可以被引用
def func(): #func = 函式的記憶體地址
print('from func')
f=func
print(f)
2、可以當作引數傳遞給另外一個函式
def func():
print('from func')
def foo(x):
print(x)
x()
foo(func)
3、可以當做函式的返回值
def func(): print('from func') def foo(x): return x res = foo(func) print(res)
4、可以當做容器型別的元素
def func():
print('from func')
l=[func,]
print(l)
l[0]()
案例===利用該特性,優雅的取代多分支的if
def withdraw(): print("提款") def tranfer(): print('轉賬') def check_balance(): print("查詢餘額") def save(): print("存款") func_dic = { "1": ["提款",withdraw], "2": ["轉賬",tranfer], "3": ["查詢餘額",check_balance], "4": ["存款",save]} while True: print("0 退出") for k,v in func_dic.items(): print(k,v[0]) choice = input(">>>: ").strip() if choice == '0': break if choice in func_dic: func_dic[choice][1]() else: print("輸入錯誤")
二、函式的巢狀
1、函式的巢狀呼叫
def max(x,y):
return x if x > y else y
def max4(a,b,c,d):
res1=max(a,b)
res2=max(res1,c)
res3=max(res2,d)
return res3
print(max4(1,2,3,4))
2、函式的巢狀定義
def f1(): x = 10 def f2(): print('from f2') print(x) print(f2) f1() print(x) print(f2)
三、名稱空間與作用域
1、什麼是名稱空間?
名稱空間:存放名字的地方,三種名稱空間
1)內建名稱空間:存放內建的名字
生命週期:python直譯器啟動則刪除,關閉則銷燬
2)全域性名稱空間:存放的是頂級的名字
生命週期:執行python檔案時則產生,python檔案執行完畢則銷燬
3)區域性名稱空間: 存放的是函式內的名字
生命週期:呼叫函式則產生,函式呼叫完畢則銷燬
2、名稱空間的載入順序
python test.py
#1、python直譯器先啟動,因而首先載入的是:內建名稱空間
#2、執行test.py檔案,然後以檔案為基礎,載入全域性名稱空間
#3、在執行檔案的過程中如果呼叫函式,則臨時產生區域性名稱空間
3、名字的查詢順序
區域性名稱空間--->全域性名稱空間--->內建名稱空間
需要注意的是:在全域性無法檢視區域性的,在區域性可以檢視全域性的,如下示例
max=1
def f1():
# max=2
def f2():
# max=3
print(max)
f2()
f1()
print(max)
核心:名字的訪問優先順序 基於當前所在的位置向外查詢 函式內->外層函式->。。。->全域性->內建
p s:名稱空間代名詞 LEGB (L代表區域性,E代表區域性之外的層,G代表全域性,B代表內建
LEGB 代表名字查詢順序: locals -> enclosing function -> globals -> __builtins__
locals 是函式內的名字空間,包括區域性變數和形參
enclosing 外部巢狀函式的名字空間(閉包中常見)
globals 全域性變數,函式定義所在模組的名字空間
builtins 內建模組的名字空間
4、作用域
#1、作用域即範圍
- 全域性範圍(內建名稱空間與全域性名稱空間屬於該範圍):全域性存活,全域性有效
- 區域性範圍(區域性名稱空間屬於該範圍):臨時存活,區域性有效
#2、稱空間與作用域的關係是在函式定義階段(掃描語法時)就確立的,與什麼時候呼叫以及呼叫位置無關,如下
x=1
def f1():
def f2():
print(x)
return f2
x=100
def f3(func):
x=2
func()
x=10000
f3(f1())
#3、檢視作用域:globals(),locals()
..global與nonlocal關鍵字
x = 111
def func():
global x # 生命變數名是來自於全域性的
x = 222
func()
print(x)
=====================
x = 111
def f1():
x = 222
def f2():
nonlocal x # 宣告變數名是來自於外層函式的,不是全域性
x = 333
f2()
print(x)
f1()
四、閉包函式
閉:指的該函式是定義在函式內的函式
包:指的就是該函式引用了一個外層函式作用域的名字
ef outter():
x = 111
def wrapper():
print(x)
return wrapper # 千萬別括號
f = outter()
print(f)
def foo():
x=222
f()
foo()
內部函式包含對外部作用域而非全域性作用域的引用
提示:之前我們都是通過引數將外部的值傳給函式,閉包提供了另外一種思路,包起來嘍,包起呦,包起來哇
def counter():
n=0
def incr():
nonlocal n
x=n
n+=1
return x
return incr
c=counter()
print(c())
print(c())
print(c())
print(c.__closure__[0].cell_contents) #檢視閉包的元素
為函式體程式碼傳參的方案
方案一:直接用引數傳
def wrapper(x):
print(x)
wrapper(111)
wrapper(222)
wrapper(333)
方案二:閉包函式
def outter(x):
x = 111
def wrapper():
print(x)
return wrapper
f1 = outter(111)
f1()
f2 = outter(222)
f2()
五、裝飾器
1、什麼是裝飾器
裝飾器就是一個用來為被裝飾物件新增新功能的工具,是閉包函式的一種應用場景
2、為何要用裝飾器
開放封閉原則:一旦軟體上線執行之後,應該對修改原始碼封閉,對擴充套件功能開放
強調裝飾器的原則:1 不修改被裝飾物件的原始碼 2 不修改被裝飾物件的呼叫方式
裝飾器的目標:在遵循1和2的前提下,為被裝飾物件新增上新功能
3、如何實現裝飾器
需求:為函式新增統計執行時間的功能
& 無參裝飾器
import time
def timmer(func):
def wrapper(*args,**kwargs):
start_time=time.time()
res=func(*args,**kwargs)
stop_time=time.time()
print('run time is %s' %(stop_time-start_time))
return res
return wrapper
@timmer
def foo():
time.sleep(3)
print('from foo')
foo()
& 無參裝飾器
& 有參裝飾器
def auth(driver='file'):
def auth2(func):
def wrapper(*args,**kwargs):
name=input("user: ")
pwd=input("pwd: ")
if driver == 'file':
if name == 'egon' and pwd == '123':
print('login successful')
res=func(*args,**kwargs)
return res
elif driver == 'ldap':
print('ldap')
return wrapper
return auth2
@auth(driver='file')
def foo(name):
print(name)
foo('egon')
& 有參裝飾器