亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢(xún)客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

裝飾器(Decorators)是 Python 的一個(gè)重要部分。簡(jiǎn)單地說(shuō):他們是修改其他函數(shù)的功能的函數(shù)。他們有助于讓我們的代碼更簡(jiǎn)短,也更Pythonic(Python范兒)。在程序開(kāi)發(fā)中經(jīng)常使用到的功能,合理使用裝飾器,能讓我們的程序如虎添翼。

私信小編01 獲取全套學(xué)習(xí)教程!

1. 函數(shù)名應(yīng)用

函數(shù)名是什么?函數(shù)名是函數(shù)的名字,本質(zhì):變量,特殊的變量。

 函數(shù)名就是函數(shù)的內(nèi)存地址,直接打印函數(shù)名,就是打印內(nèi)存地址

def func1():
 print(123)
print(func1)         # <function func1 at 0x0000029042E02E18>

 函數(shù)名可以作為變量

def func1():
 print(111)
f = func1
f()           # f() 就是func1() 

 函數(shù)名可以作為函數(shù)的參數(shù)

def func1():
 print(111)
def func2(x):
 x()
func2(func1)         #func1作為func2的參數(shù) 

 函數(shù)名可以作為函數(shù)的返回值

def wrApper():
 def inner():
 print('inner')
 return inner
f = wrapper()
f()

 函數(shù)名可以作為容器類(lèi)類(lèi)型的元素

使用for循環(huán)批量執(zhí)行函數(shù)
def func1():
 print('func1')
def func2():
 print('func2')
def func3():
 print('func3')
l1 = [func1,func2,func3]
for i in l1:
 i()

像上面函數(shù)名這種,叫做第一類(lèi)對(duì)象。

第一類(lèi)對(duì)象( first-class object)指:

1.可在運(yùn)行期創(chuàng)建

2.可用作函數(shù)參數(shù)或返回值

3.可存入變量的實(shí)體

*不明白?那就記住一句話,就當(dāng)普通變量用

2. 閉包

1、 閉包函數(shù) : 內(nèi)部函數(shù)包含對(duì)外部作用域而非全局作用域變量的引用,該內(nèi)部函數(shù)稱(chēng)為閉包函數(shù)

2、閉包的作用:爬蟲(chóng)、裝飾器

當(dāng)程序執(zhí)行遇到函數(shù)執(zhí)行時(shí),會(huì)在內(nèi)存空間開(kāi)辟局部命名空間,當(dāng)函數(shù)執(zhí)行完畢,該命名空間會(huì)被銷(xiāo)毀。但是如果這個(gè)函數(shù)內(nèi)部形成閉包,則該內(nèi)存空間不會(huì)隨著函數(shù)執(zhí)行完而消失。

3、如何判斷是否是閉包:print(函數(shù)名.__closure__) 結(jié)果是 cell 說(shuō)明是閉包,結(jié)果是 None說(shuō)明不是閉包。

閉包舉例

def wrapper():
 name = 'summer'
 def inner():
 print(name)
 inner()
wrapper() # summer

如何判斷它是否是一個(gè)閉包函數(shù)呢? 內(nèi)層函數(shù)名.__closure__ cell 就是=閉包

例 1.

def wrapper():
 name = 'summer'
 def inner():
 print(name)
 inner()
 print(inner.__closure__)
wrapper() 
執(zhí)行輸出:
summer
(<cell at 0x0000017FC9C90B58: str object at 0x0000017FCA349AD0>,)

例 2.

name = 'summer'
def wrapper():
 def inner():
 print(name)
 inner()
 print(inner.__closure__)
wrapper() 
結(jié)果輸出:
summer
None

返回值為None 表示它不是閉包,因?yàn)閚ame是一個(gè)全局變量,如果函數(shù)調(diào)用了外層變量而非全局變量,那么它就是閉包。

例 3.

name = 'summer'
def wrapper2():
 name1 = 'spring'
 def inner():
 print(name)
 print(name1)
 inner()
 print(inner.__closure__)
wrapper2()
結(jié)果輸出:
summer
spring
(<cell at 0x030B7310: str object at 0x03043680>,)

只要引用了外層變量至少一次,非全局的,它就是閉包

例 4:判斷 下面的函數(shù),是一個(gè)閉包嗎? ******

name = 'summer'
def wraaper2(n):        #相當(dāng)于n = 'summer' 
  def inner():
 print(n)
 inner()
 print(inner.__closure__) 
wraaper2(name)
結(jié)果輸出:
summer
(<cell at 0x03867350: str object at 0x037F3680>,)

它也是一個(gè)閉包. 雖然wraaper2傳了一個(gè)全局變量,但是在函數(shù)wraaper2內(nèi)部,inner引用了外層變量,相當(dāng)于在函數(shù)inner外層定義了 n = 'summer',所以inner是一個(gè)閉包函數(shù)

閉包的好處  當(dāng)函數(shù)開(kāi)始執(zhí)行時(shí),如果遇到了閉包,他有一個(gè)機(jī)制,他會(huì)永遠(yuǎn)開(kāi)辟一個(gè)內(nèi)存空間,將閉包中的變量等值放入其中,不會(huì)隨著函數(shù)的執(zhí)行完畢而消失。

舉一個(gè)例子:爬3次,內(nèi)存開(kāi)了3次,很占用內(nèi)存

from urllib.request import urlopen
content1 = urlopen('https://www.cnblogs.com/').read().decode('utf-8')
content2 = urlopen('https://www.cnblogs.com/').read().decode('utf-8')
content3 = urlopen('https://www.cnblogs.com/').read().decode('utf-8')

把它封裝成閉包

from urllib.request import urlopen
def index():
 url = "https://www.cnblogs.com/"
 def get():
 return urlopen(url).read()
 return get        #return的是get,就是一個(gè)函數(shù)名
cnblog = index()
print(cnblog) # <function index.<locals>.get at 0x02F46978>
content = cnblog()
print(content) # 頁(yè)面源碼

這個(gè)例子,只有第一遍,是從網(wǎng)站抓取的。之后的執(zhí)行,直接從內(nèi)存中加載,節(jié)省內(nèi)存空間

3.裝飾器

3.1 裝飾器初識(shí)

裝飾器本質(zhì): 就是一個(gè)python函數(shù),他可以讓其他函數(shù)在不需要做任何代碼變動(dòng)的前提下,增加額外的功能,裝飾器的返回值也是一個(gè)函數(shù)對(duì)象。

裝飾器的應(yīng)用場(chǎng)景:比如插入日志,性能測(cè)試,事務(wù)處理,緩存等等場(chǎng)景。

import time
def timmer(f):       
 def inner():
 start_time = time.time() 
 f()      
 end_time = time.time() 
 print('此函數(shù)的執(zhí)行時(shí)間為{}'.format(end_time - start_time)) 
 return inner   
def func1():   
 print('in func1')       
 time.sleep(1)       
func1 = timmer(func1)       
print(func1)
func1()           # 這里的func1是全新的func1,就是上面的賦值,此時(shí)相當(dāng)于執(zhí)行 inner函數(shù)
輸出結(jié)果:
<function timmer.<locals>.inner at 0x03822DF8>
in func1
此函數(shù)的執(zhí)行時(shí)間為1.0003533363342285

代碼從上至下執(zhí)行

語(yǔ)法糖: 想測(cè)試誰(shuí),前面加@裝飾器函數(shù),即可。 寫(xiě)裝飾器,約定俗成,函數(shù)名為wrapper

def wrapper(func):
 def inner(*args,**kwargs):
 '''被裝飾函數(shù)之前'''
 ret = func(*args,**kwargs)
 '''被裝飾函數(shù)之后'''
 return ret
 return inner
@wrapper
def func(*args,**kwargs):
 print(args,kwargs)
 return 666
print(func())
輸出結(jié)果:
() {}
666

裝飾器利用return制造了一個(gè)假象,func()執(zhí)行,其實(shí)是執(zhí)行inner()  func()把原來(lái)的func()給覆蓋了

3.2 裝飾器傳參

例 1: 上面裝飾器的例子,func1,要傳2個(gè)參數(shù)a,b

import time
def timmer(f):
 def inner(a,b):
 start_time = time.time()
 f(a,b)
 end_time = time.time()
 print('此函數(shù)的執(zhí)行時(shí)間為{}'.format(end_time - start_time))
 return inner
@timmer
def func1(a,b):
 print('in func1 {}{}'.format(a,b))
 time.sleep(1) # 模擬程序邏輯
func1(1,2) 
執(zhí)行輸出:
in func1 12
此函數(shù)的執(zhí)行時(shí)間為1.0006024837493896

例 2: 如果有多個(gè)參數(shù)呢?改成動(dòng)態(tài)參數(shù)

import time
def timmer(f):
 def inner(*args,**kwargs):
 start_time = time.time()
 f(*args,**kwargs)
 end_time = time.time()
 print('此函數(shù)的執(zhí)行時(shí)間為{}'.format(end_time - start_time))
 return inner
@timmer
def func1(*args,**kwargs):
 print('in func1 {}{}'.format(args,kwargs))
 time.sleep(1) # 模擬程序邏輯
func1(1,2,a='3',b=4) 
執(zhí)行輸出:
in func1 (1, 2){'b': 4, 'a': '3'}
此函數(shù)的執(zhí)行時(shí)間為1.000101089477539

函數(shù)的執(zhí)行時(shí),*打散 ;

函數(shù)的定義時(shí),*聚合。

from functools import wraps
def wrapper(f): # f = func1
 def inner(*args,**kwargs):       #聚合,args (1,2,3)
 '''執(zhí)行函數(shù)之前的相關(guān)操作'''
 ret = f(*args,**kwargs)      # 打散 1,2,3
 '''執(zhí)行函數(shù)之后的相關(guān)操作'''
 return ret
 return inner
@wrapper # func1 = wrapper(func1) func1 = inner
def func1(*args):       #args (1,2,3) 聚合
 print(666)
 return args
print(func1(*[1,2,3])) 
執(zhí)行輸出:
666
(1, 2, 3)

例 3 *****

import time #1.加載模塊
def timmer(*args,**kwargs): #2.加載變量 5.接收參數(shù)True,2,3
 def wrapper(f): #6.加載變量 8.f = func1
 print(args, kwargs) #9.接收timmer函數(shù)的值True,2,3
 def inner(*args,**kwargs): #10.加載變量. 13.執(zhí)行函數(shù)inner
 if flag: #14 flag = True
 start_time = time.time() #15 獲取當(dāng)前時(shí)間
 ret = f(*args,**kwargs) #16 執(zhí)行func1
 time.sleep(0.3) #19 等待0.3秒
 end_time = time.time() #20 獲取當(dāng)前時(shí)間
 print('此函數(shù)的執(zhí)行效率%f' % (end_time-start_time)) #21 打印差值
 else:
 ret = f(*args, **kwargs)
 return ret #22 返回給函數(shù)調(diào)用者func1()
 return inner #11 返回給函數(shù)調(diào)用者wrapper
 return wrapper #7.返回給函數(shù)調(diào)用timmer(flag,2,3)
flag = True #3 加載變量
@timmer(flag,2,3) # 4.執(zhí)行函數(shù)timmer(flag,2,3) 17.執(zhí)行函數(shù)func1 兩步:1,timmer(flag,2,3) 相當(dāng)于執(zhí)行wrapper 2.@wrapper 裝飾器 func1 = wrapper(func1)
def func1(*args,**kwargs):
 return 666 #18 返回給函數(shù)調(diào)用者f(*args,**kwargs)
print(func1()) #12 執(zhí)行函數(shù) 

寫(xiě)裝飾器,一般嵌套3層就可以了

3.3 多個(gè)裝飾器,裝飾一個(gè)函數(shù)

def wrapper1(func): # func == f函數(shù)名
 def inner1():
 print('wrapper1 ,before func') # 2
 func()
 print('wrapper1 ,after func') # 4
 return inner1
def wrapper2(func): # func == inner1
 def inner2():
 print('wrapper2 ,before func') # 1
 func()
 print('wrapper2 ,after func') # 5
 return inner2
@wrapper2 # f = wrapper2(f) 里面的f==inner1 外面的f == inner2
@wrapper1 # f = wrapper1(f) 里面的f==函數(shù)名f 外面的f == inner1
def f(): # 3
 print('in f')
f() # inner2() 
執(zhí)行輸出:
wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func

哪個(gè)離函數(shù)近,哪個(gè)先計(jì)算  最底下的先執(zhí)行

執(zhí)行順序如下圖:

這是我見(jiàn)過(guò)最全面的Python裝飾器教程了

 

多個(gè)裝飾器,都是按照上圖的順序來(lái)的

4. 裝飾器的 __name__  __doc___

__name__:函數(shù)名

__doc___:函數(shù)的解釋

普通函數(shù)

def func1():
 """
 此函數(shù)是完成登陸的功能,參數(shù)分別是...作用。
 return: 返回值是登陸成功與否(True,F(xiàn)alse)
 """
 print(666)
func1()
print(func1.__name__) #獲取函數(shù)名
print(func1.__doc__) #獲取函數(shù)名注釋說(shuō)明 
執(zhí)行輸出:
666
func1
此函數(shù)是完成登陸的功能,參數(shù)分別是...作用。
return: 返回值是登陸成功與否(True,F(xiàn)alse)

這個(gè)有什么用呢?比如日志功能,需要打印出誰(shuí)在什么時(shí)間,調(diào)用了什么函數(shù),函數(shù)是干啥的,花費(fèi)了多次時(shí)間,這個(gè)時(shí)候,就需要獲取函數(shù)的有用信息了

帶裝飾器的函數(shù)

def wrapper(f): # f = func1
 def inner(*args,**kwargs): #聚合, args (1,2,3)
 '''執(zhí)行函數(shù)之前的相關(guān)操作'''
 ret = f(*args,**kwargs) # 打散 1,2,3
 '''執(zhí)行函數(shù)之后的相關(guān)操作'''
 return ret
 return inner
@wrapper
def func1():
 """
 此函數(shù)是完成登陸的功能,參數(shù)分別是...作用。
 return: 返回值是登陸成功與否(True,F(xiàn)alse)
 """
 print(666)
 return True
func1()
print(func1.__name__)
print(func1.__doc__) 
執(zhí)行輸出:
666
inner
執(zhí)行函數(shù)之前的相關(guān)操作

函數(shù)裝飾之后,相當(dāng)于執(zhí)行了inner函數(shù),所以輸出inner

為了解決這個(gè)問(wèn)題,需要 調(diào)用一個(gè)模塊wraps

wraps將 被修飾的函數(shù)(wrapped) 的一些屬性值賦值給修飾器函數(shù)(wrapper) ,最終讓屬性的顯示更符合我們的直覺(jué)

from functools import wraps
def wrapper(f): # f = func1
 @wraps(f) #f是被裝飾的函數(shù)
 def inner(*args,**kwargs): #聚合args (1,2,3)
 '''執(zhí)行函數(shù)之前的相關(guān)操作'''
 ret = f(*args,**kwargs) # 打散 1,2,3
 '''執(zhí)行函數(shù)之后的相關(guān)操作'''
 return ret
 return inner
@wrapper
def func1():
 """
 此函數(shù)是完成登陸的功能,參數(shù)分別是...作用。
 return: 返回值是登陸成功與否(True,F(xiàn)alse)
 """
 print(666)
 return True
func1()
print(func1.__name__)
print(func1.__doc__) 
執(zhí)行輸出:
666
func1
此函數(shù)是完成登陸的功能,參數(shù)分別是...作用。
return: 返回值是登陸成功與否(True,F(xiàn)alse)

分享到:
標(biāo)簽:裝飾 Python
用戶(hù)無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定