<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
不能修改被裝飾的函數的原始碼(比如線上環境)
不能修改被裝飾的函數的呼叫方式
函數即是“變數”
高階函數
巢狀函數
高階函數+巢狀函數=>裝飾器
先分析以下兩段程式碼能不能執行?
def foo(): print("in the foo") bar() def bar(): print("in the bar") foo()
def foo(): print("in the foo") bar() foo() def bar(): print("in the bar")
第二段程式碼報錯:
NameError: name 'bar' is not defined
定義變數:
如:定義變數:x=1,會在記憶體中找塊記憶體空間把“1”存進去,把“1”的記憶體地址給x
前面提到:函數即變數
# 定義函數 def test(): pass # 就相當於把函數體賦值給test變數 test = '函數體' # 函數體就是一堆字串而已 # 只不過函數呼叫要加上小括號呼叫 test()
python記憶體回收機制,是直譯器做的。直譯器到底怎麼去回收這個變數?
python直譯器當中有種概念叫做參照計數。什麼叫參照計數呢?
比如:定義x=1,之後又定義了y=1或y=x,實際上又把記憶體空間“1”的記憶體地址賦值給y
這裡x代表一次參照,y代表一次參照。加起來兩次參照。
python什麼時候會把“1”這個記憶體空間清空呢?會回收記憶體呢?
當x這個變數沒有了,y這個變數也沒有了,便會把“1”這個記憶體空間清掉
del x # 刪的只是變數名,記憶體中的值是直譯器回收
匿名函數
lambda x:x*x
匿名函數沒有函數名,沒有參照,所以會被垃圾回收機制立馬回收掉。
所以匿名函數要賦值給變數,把函數體賦值給變數名
calc = lambda x:x*x print(calc(4))
現在可以再理解下最開始兩段程式碼能不能執行的原因。
什麼叫高階函數呢:
def f1(): print("in the func1") def test1(func): print(func) test1(f1)
執行結果(列印記憶體地址)
<function func1 at 0x000002805DE12378>
如下程式碼,能不能執行:
def f1(): print("in the func1") def test1(func): print(func) func() test1(f1)
函數即變數,像“x=1,y=x”,同樣f是一個是一個函數,可不可以像一個變數一樣來回賦值呢?
import time def func1(): print("in the func1") time.sleep(1) def test1(func): start_time = time.time() func() stop_time = time.time() print("the func run time is %s" %(stop_time-start_time)) test1(func1)
到這裡,貌似實現了裝飾函數的功能。
看上面裝飾器的原則:
這裡:沒有修改func1的原始碼,但是呼叫方式改變了。現在是test1(func1),之前是func1()
現在能做到哪一點呢?
把一個函數名當做實參傳給另外一個函數(不修改被裝飾的函數原始碼的情況下為其新增功能)
2) 下面用第二個條件(返回值中包含函數名),做另外一個高階函數
import time def func2(): time.sleep(1) print("in the func2") def test2(func): print(func) return(func) print(test2(func2))
執行結果:
<function func2 at 0x00000162F3672378>
<function func2 at 0x00000162F3672378>
把函數記憶體地址都列印出來了,看到這麼多記憶體地址,有什麼想法?
加上小括號就能執行。
上面程式碼“test2(func2())”和“test2(func2)”有什麼區別?加上小括號是函數返回結果,不加是函數記憶體地址。所以加上小括號就不符合高階函數定義了。
既然以後有了函數的記憶體地址,是不是可以賦值給其他變數?下面
import time def func2(): print("in the func2") time.sleep(1) def test2(func): print(func) return(func) t = test2(func2) print(t) t()
好像還沒什麼用,怎麼讓他有用呢?
把test2(func2)賦值給func2
import time def func2(): print("in the func2") time.sleep(1) def test2(func): print(func) return(func) func2 = (test2(func2)) func2()
這就是高階函數的第二個好處:返回值中包含函數名(不修改函數的呼叫方式)
巢狀函數:在一個函數體內,用def去宣告一個函數
def foo(): print("in the foo") def bar(): print("in the bar") bar() foo()
看一下下面的程式碼是不是巢狀:
def foo(): print("in the foo") def bar(): foo() bar()
注意函數巢狀和函數呼叫區別
區域性作用域和全域性作用域的存取順序:
x = 0 def grandpa(): # x = 1 def dad(): x = 2 def son(): x = 3 print(x) son() dad() grandpa()
前面鋪墊了那麼多,現在開講正題:裝飾器
先用高階函數實現給函數不修改原始碼的情況下新增功能
import time def deco(func): start_time = time.time() func() stop_time = time.time() print("the func tun time is %s" %(stop_time-start_time)) def test1(): time.sleep(1) print("in the test1") def test2(): time.sleep(1) print("in the test2") deco(test1) deco(test2)
按照上面說的,如何實現不改變呼叫方式?直接“test1 = deco(test1)”和“test2 = deco(test2)”嗎?
別忘記了,第二種方式,高階函數要加上return,如下
import time def deco(func): start_time = time.time() return func() stop_time = time.time() print("the func tun time is %s" %(stop_time-start_time)) def test1(): time.sleep(1) print("in the test1") def test2(): time.sleep(1) print("in the test2") test1 = deco(test1) test2 = deco(test2) deco(test1) deco(test2)
雖然沒有修改原始碼和呼叫方式,但是函數加上return,函數就結束了,然並卵。怎麼實現呢?
前面一直在用高階函數,還沒有用巢狀函數,加上巢狀函數能不能實現呢?看一下
import time def timer(func): # timer(test1) func=test1 def deco(): start_time = time.time() func() stop_time = time.time() print("the func tun time is %s" %(stop_time-start_time)) return deco # 返回deco的記憶體地址 def test1(): time.sleep(1) print("in the test1") def test2(): time.sleep(1) print("in the test2") print(timer(test1)) # 可見:返回deco的記憶體地址 test1 = timer(test1) test1() timer(test2)()
到此,完成實現了裝飾器的功能。但是還是有點麻煩,如何能不要“test1 = timer(test1)”,
python直譯器提供了語法糖“@”符合,給哪個函數新增功能,就加在哪個函數頭部
import time def timer(func): # timer(test1) func=test1 def deco(): start_time = time.time() func() stop_time = time.time() print("the func tun time is %s" %(stop_time-start_time)) return deco # 返回deco的記憶體地址 @timer def test1(): time.sleep(1) print("in the test1") @timer def test2(): time.sleep(1) print("in the test2") test1() test2()
前面實現了裝飾器的功能,但是如果函數有引數,能不能也能執行呢
import time def timer(func): # timer(test1) func=test1 def deco(): start_time = time.time() func() stop_time = time.time() print("the func tun time is %s" %(stop_time-start_time)) return deco # 返回deco的記憶體地址 @timer def test1(): time.sleep(1) print("in the test1") @timer def test2(name): time.sleep(1) print("in the test2",name) test1() test2()
報錯:丟失引數
TypeError: test2() missing 1 required positional argument: 'name'
@timer 相當於 test2=timer(test2) =deco
test2() 相當於執行deco(),所以沒指定引數,報錯。
如何傳引數呢?為了適應各種不同引數的函數
import time def timer(func): # timer(test1) func=test1 def deco(*args,**kwargs): start_time = time.time() func(*args,**kwargs) stop_time = time.time() print("the func tun time is %s" %(stop_time-start_time)) return deco # 返回deco的記憶體地址 @timer def test1(): time.sleep(1) print("in the test1") @timer def test2(name): time.sleep(1) print("in the test2",name) test1() test2("fgf")
注意,上面的例子中還沒有涉及返回值,看下面的例子可以體會一下
假設:公司網站需要驗證登入,有不同的驗證方式:本地認證、LDAP認證等
#/usr/bin/env python # -*- coding: UTF-8 -*- import time user,passwd = 'fgf','abc123' def auth(auth_type): print("auth func:",auth_type) def outer_wrapper(func): def wrapper(*args, **kwargs): print("wrapper func args:", *args, **kwargs) if auth_type == "local": username = input("Username:").strip() password = input("Password:").strip() if user == username and passwd == password: print(" 33[32;1mUser has passed authentication 33[0m") res = func(*args, **kwargs) # from home print("---after authenticaion ") return res else: exit(" 33[31;1mInvalid username or password 33[0m") elif auth_type == "ldap": print("搞毛線ldap,不會。。。。") return wrapper return outer_wrapper def index(): print("welcome to index page") @auth(auth_type="local") # home = wrapper() def home(): print("welcome to home page") return "from home" @auth(auth_type="ldap") def bbs(): print("welcome to bbs page") index() print(home()) #wrapper() bbs()
到此這篇關於Python裝飾器詳細講解的文章就介紹到這了,更多相關Python裝飾器內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45