目錄
1、閉包
1.1、什麼是閉包?
1.2、形成閉包的條件
1.3、閉包的使用
1.4、閉包的結論和好處
2、裝飾器
2.1、什麼是裝飾器?
2.2、裝飾器有什麼用?
2.2.1、編寫一個裝飾器實現權限控制
2.2.2、多個裝飾器的使用
2.3、元數據
2.3.1、裝飾器後為什麼元數據會丟失以及怎麼保留元數據
3、裝飾器帶參數
3.1、裝飾器帶參數的使用方法
3.1.1、用裝飾器帶參數的方法重新編寫上文中權限控制代碼
4、用類實現裝飾器和裝飾器帶參數
4.1、用類實現裝飾器
4.2、用類實現裝飾器帶參數
5、裝飾類
1、必須要有一個內嵌函數
2、內函數必須引用外函數的變量
3、外函數必須返回內函數(注意這裡是返回內函數,而不是內函數的調用)
# 生命周期
def outer():
a = 10
print(f"a is {a}")
outer()
print(a) # 這行代碼運行會出現錯誤
# 這個代碼運行會出現錯誤,是因為外部並沒有定義a這個變量。內部使用完a這個變量,就已經把這個變量釋放掉了。
def outer(x):
a = 300 # 變量名的解析原則LEGB
def inner():
# x = 90
print(f"兩數之和為:{x + a}") # 運行結果為310
print(__name__) #運行結果為__main__
return inner
d = outer(10)
d()
print(dir(d))
print(d.__closure__) # 輸出的結果是一個元組,裡面存儲著變量的內存地址,
# 形成閉包之後,閉包函數會得到一個非空的__closure__屬性
def outer():
tmp_list = []
def inner(name):
tmp_list.append(1)
print(f"{name} -- {tmp_list}")
return inner
d1 = outer()
d2 = outer()
d1("d1")
d2("d2")
print(id(d1),id(d2))
# 雖然代碼一樣,但是每次調用外函數都會重新執行,都會創建一個新的tmp_list和inner
print(id(outer()),id(outer())) # 這個是匿名變量
print(id(d1),id(d2))
# python變量分為匿名變量和命名變量
# 匿名變量一般都放到統一的內存空間
# 運行結果
d1 -- [1]
d2 -- [1]
2383138097616 2383138098048
2383426954432 2383426954432
2383138097616 2383138098048
結論:
好處:
定義:裝飾器是一種程序設計模式,它的本質就是閉包,它在不改變函數或者類的源代碼的基礎上,添加額外功能。
條件:
def login_required(func):
def inner(*args,**kwargs):
if username.lower() == "root":
result = func(*args,**kwargs)
print(f"歡迎執行{func.__name__}函數")
return result
else:
return f"執行{func.__name__}沒有權限"
return inner
username = input("請輸入你的用戶名:")
# 函數被修飾符裝飾後,返回的都是內函數的值
@login_required # add = login_required(add),相當於執行了這個操作。
def add(a=int(input("請輸入加數1:")),b=int(input("請輸入加數2:"))):
time.sleep(2)
return f"兩數和為:{a+b}"
print(add())
######## 執行結果
請輸入你的用戶名:root
請輸入加數1:2
請輸入加數2:3
歡迎執行add函數
兩數和為:5
請輸入你的用戶名:fdjk
請輸入加數1:1
請輸入加數2:2
執行add沒有權限
可以應用多個裝飾器,但是要注意裝飾器的執行順序:從最上面的裝飾器的內函數開始執行
# 統計運行時間的裝飾器
import time
import functools
def runtime(func):
# 保留傳遞進來的函數的元數據,將它的元素據賦值給inner。元數據為原來的名字、文檔注釋等。
@functools.wraps(func)
def inner(*args,**kwargs): # 加上可變長位置參數可以讓裝飾器更加的通用
start_time = time.time() # 這個是時間戳,從1970年一月一日到至今的總秒數
result = func(*args,**kwargs)
end_time = time.time()
print(f"函數執行花了{end_time-start_time}s")
return result
return inner
def login_required(func):
def inner(*args, **kwargs):
if username == 'root':
print(f"歡迎執行{func.__name__}函數") # 這裡要是沒有functools.wraps(func),執行結果就會變成"歡迎執行inner函數"
result = func(*args, **kwargs)
return result
else:
return f"執行{func.__name__}權限不夠"
return inner
# 限制性login_required的內部函數,執行到func,就跳到runtime的內函數
@login_required # add = login_required(runtime(add))
@runtime # add = runtime(add)
def add( a, b):
return a + b
username = input("請輸入您要使用的用戶:")
a = int(input("請輸入要計算的數字1:"))
b = int(input("請輸入要計算的數字2:"))
print(add(a, b))
###### 執行結果
請輸入您要使用的用戶:root
請輸入要計算的數字1:2
請輸入要計算的數字2:2
歡迎執行add函數
函數執行花了0.0s
4
上面這個練習中使用到了" @functools.wraps(func)",這個東東是用來保留元數據的。
定義:元數據(Metadata),又稱中介數據、中繼數據,為描述數據的數據(data about data),主要是描述數據屬性(property)的信息
函數的重要的元信息,比如:名字、文檔注釋、字符串和參數簽名等。
為什麼會丟失
怎麼保留元數據
溫馨提示:任何時候你定義裝飾器的時候,都應該使用functools庫中的@wraps裝飾器來注釋底層包裝函數。
原則:
錯誤使用方法:
import functools
import time
def runtime(func):
#保留傳遞進來的函數的元數據,將它的元數據賦值給inner
@functools.wraps(func)
def inner(*args, **kwargs): #讓裝飾器更加通用
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"函數執行花了{end -start}s")
return result
return inner
# 若是兩層的帶了參數的話,是這麼執行的
# a = runtime("name")
# add = a(add)
@runtime("name") # 對於兩層的函數,這樣寫是會報錯的。因為func若是為字符串的,func(*args, **kwargs)就沒有意義。因為字符串不是一個可調用對象(callable)
def add(a,b):
return a+b
正確使用方法:
import functools
import time
def decp(name):
def runtime(func):
#保留傳遞進來的函數的元數據,將它的元數據賦值給inner
@functools.wraps(func)
def inner(*args, **kwargs): #讓裝飾器更加通用
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"函數執行花了{end -start}s")
print(f"name is {name}")
print("1")
return result
print("2")
return inner
print("3")
return runtime
@decp(name="sc") # 這個相當於# runtime = deco(name="sc"), func1 = runtime(func1)
def func1():
time.sleep(3)
print("this is func1")
func1()
### 執行結果
3
2
this is func1
函數執行花了8.803873777389526s
name is sc
1
import time
def test(username):
def login_required(func):
def inner(*args, **kwargs):
if username == 'root':
print(f"歡迎執行{func.__name__}函數")
result = func(*args, **kwargs)
return result
else:
return f"執行{func.__name__}權限不夠"
return inner
return login_required
@test(username=input("請輸入你的用戶名:"))
def add(a=int(input("請輸入加數1:")),b=int(input("請輸入加數2:"))):
time.sleep(2)
return f"兩數和為:{a+b}"
result = add()
print(result)
# 執行結果
請輸入你的用戶名:root
請輸入加數1:2
請輸入加數2:3
歡迎執行add函數
兩數和為:5
class rumtime():
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
start = time.time()
self.func()
end = time.time()
print(f"用時{end-start}")
@rumtime
def add():
time.sleep(2)
print("day day up")
add()
#### 執行結果
day day up
用時2.001725912094116
class rumtime():
def __init__(self, name):
self.func = name
def __call__(self, func):
def deco(*args,**kwargs):
start = time.time()
result = func(*args,**kwargs)
end = time.time()
print(f"用時{end-start}")
return result
return deco
@rumtime("name")
def add():
time.sleep(2)
print("day day up")
add()
# 執行結果
day day up
用時2.0109517574310303
def outer(cls):
def inner(*args,**kwargs):
print(f"class name is:{cls.__name__}")
return cls(*args,**kwargs)
return inner
@outer # A = outer(A)
class A:
def __init__(self, name):
self.name = name
print(type(A))
m = A("sc")
print(m.name)
### 執行結果
<class 'function'>
class name is:A
sc