中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Python實戰教程:修飾器精講!(2019下半年篇)

發布時間:2020-08-10 13:43:18 來源:ITPUB博客 閱讀:182 作者:千鋒Python唐小強 欄目:編程語言

是不是很多伙伴都認為Python的語法簡單,作為入門語言學起來非常簡單?

很多伙伴說Python寫出來的代碼只要符合邏輯,不需要太多的學習即可,即可從一門其他語言跳來用Python寫(當然這樣是好事,誰都希望入門簡單)。

于是我便記錄一下,如果要學Python的話,到底有什么好學的。記錄一下Python有什么值得學的,對比其他語言有什么特別的地方,有什么樣的代碼寫出來更Pythonic。一路回味,一路學習。

什么是修飾器,為什么叫修飾器

修飾器英文是Decorator,

我們假設這樣一種場景:古老的代碼中有幾個很是復雜的函數F1、F2、F3…,復雜到看都不想看,反正我們就是不想改這些函數,但是我們需要改造加功能,在這個函數的前后加功能,這個時候我們很容易就實現這個需求:

def hi():
 """hi func,假裝是很復雜的函數"""
 return 'hi'
def aop(func):
 """aop func"""
 print('before func')
 print(func())
 print('after func')
 
if __name__ == '__main__':
 aop(hi)
Python實戰教程:修飾器精講!(2019下半年篇)

以上是很是簡單的實現,利用Python參數可以傳函數引用的特性,就可以實現了這種類似AOP的效果。

這段代碼目前沒有什么問題,接下來煎魚加需求:需求為幾十個函數都加上這樣的前后的功能,而所有調用這些函數地方也要相應地升級。

看起來這個需求比較扯,偏偏這個需求卻是較為廣泛:在調用函數的前后加上log輸出、在調用函數的前后計算調用時間、在調用函數的前后占用和釋放資源等等。

一種比較笨的方法就是,為這幾十個函數逐一添加一個入口函數,針對a函數添加一個a_aop函數,針對b函數添加一個b_aop函數…如此這樣。 問題也很明顯:

  1. 工作量大
  2. 代碼變得臃腫復雜
  3. 原代碼有多處調用了這些函數,可以會升級不完全

于是接下來有請修飾器出場,修飾器可以統一地給這些函數加這樣的功能:

def aop(func):
 """aop func"""
 def wrapper():
 """wrapper func"""
 print('before func')
 func()
 print('after func')
 return wrapper
@aop
def hi():
 """hi func"""
 print('hi')
 
@aop
def hello():
 """hello func"""
 print('hello')
if __name__ == '__main__':
 hi()
 hello()
Python實戰教程:修飾器精講!(2019下半年篇)

以上aop函數就是修飾器的函數,使用該修飾器時只要在待加函數上一行加@修飾器函數名即可,如實例代碼中就是@aop。

加上了@aop后,調用新功能的hi函數就喝原來的調用一樣:就是hi()而不是aop(hi),也意味著所有調用這些函數的地方不需要修改就可以升級。

簡單地來說,大概修飾器就是以上的這樣子。

@是個什么

對于新手來說,上面例子中, @就是一樣奇怪的東西:為什么這樣子用就可以實現需求的功能了。

其實我們還可以不用@,這里換一種寫法:

def hi():
 """hi func"""
 print('hi')
def aop(func):
 """aop func"""
 def wrapper():
 """wrapper func"""
 print('before func')
 func()
 print('after func')
 return wrapper
if __name__ == '__main__':
 hi()
 print('')
 hi = aop(hi)
 hi()
Python實戰教程:修飾器精講!(2019下半年篇)

上面的例子中的aop函數就是之前說過的修飾器函數。

如例子main函數中第一次調用hi函數時,由于hi函數沒叫修飾器,因此我們可以從輸出結果中看到程序只輸出了一個hi而沒有前后功能。

然后加了一個hi = aop(hi)后再調用hi函數,得到的輸出結果和加修飾器的一樣,換言之:

@aop 等效于hi = aop(hi)

因此,我們對于@,可以理解是,它通過閉包的方式把新函數的引用賦值給了原來函數的引用。

有點拗口。aop(hi)是新函數的引用,至于返回了引用的原因是aop函數中運用閉包返回了函數引用。而hi這個函數的引用,本來是指向舊函數的,通過hi = aop(hi)賦值后,就指向新函數了。

被調函數加參數

以上的例子中,我們都假設被調函數是無參的,如hi、hello函數都是無參的,我們再看一眼煎魚剛才的寫的修飾器函數:

def aop(func):
 """aop func"""
 def wrapper():
 """wrapper func"""
 print('before func')
 func()
 print('after func')
 return wrapper

很明顯,閉包函數wrapper中,調用被調函數用的是func(),是無參的。同時就意味著,如果func是一個帶參數的函數,再用這個修飾器就會報錯。

@aop
def hi_with_deco(a):
 """hi func"""
 print('hi' + str(a))
if __name__ == '__main__':
 # hi()
 hi_with_deco(1)
Python實戰教程:修飾器精講!(2019下半年篇)

就是參數的問題。這個時候,我們把修飾器函數改得通用一點即可,其中import了一個函數(也是修飾器函數):

from functools import wraps
def aop(func):
 """aop func"""
 @wraps(func)
 def wrap(*args, **kwargs):
 print('before')
 func(*args, **kwargs)
 print('after')
 return wrap
@aop
def hi(a, b, c):
 """hi func"""
 print('test hi: %s, %s, %s' % (a, b, c))
@aop
def hello(a, b):
 """hello func"""
 print('test hello: %s, %s' % (a, b))
if __name__ == '__main__':
 hi(1, 2, 3)
 hello('a', 'b')
Python實戰教程:修飾器精講!(2019下半年篇)

這是一種很奇妙的東西,就是在寫修飾器函數的時候,還用了別的修飾器函數。那也沒什么,畢竟修飾器函數也是函數啊,有什么所謂。

帶參數的修飾器

思路到了這里,煎魚不禁思考一個問題:修飾器函數也是函數,那函數也是應該能傳參的。函數傳參的話,不同的參數可以輸出不同的結果,那么,修飾器函數傳參的話,不同的參數會怎么樣呢?

其實很簡單,修飾器函數不同的參數,能生成不同的修飾器啊。

如,我這次用這個修飾器是把時間日志打到test.log,而下次用修飾器的時候煎魚希望是能打到test2.log。這樣的需求,除了寫兩個修飾器函數外,還可以給修飾器加參數選項:

from functools import wraps
def aop_with_param(aop_test_str):
 def aop(func):
 """aop func"""
 @wraps(func)
 def wrap(*args, **kwargs):
 print('before ' + str(aop_test_str))
 func(*args, **kwargs)
 print('after ' + str(aop_test_str))
 return wrap
 return aop
@aop_with_param('abc')
def hi(a, b, c):
 """hi func"""
 print('test hi: %s, %s, %s' % (a, b, c))
@aop_with_param('pppppp')
def hi2(a, b, c):
 """hi func"""
 print('test hi: %s, %s, %s' % (a, b, c))
if __name__ == '__main__':
 hi(1, 2, 3)
 print('')
 hi2(2, 3, 4)
Python實戰教程:修飾器精講!(2019下半年篇)

同樣的,可以加一個參數,也可以加多個參數,這里就不說了。

修飾器類

大道同歸,邏輯復雜了之后,人們都喜歡將函數的思維層面抽象上升到對象的層面。原因往往是對象能擁有多個函數,對象往往能管理更復雜的業務邏輯。

顯然,修飾器函數也有對應的修飾器類。寫起來也沒什么難度,和之前的生成器一樣簡單:

from functools import wraps
class aop(object):
 def __init__(self, aop_test_str):
 self.aop_test_str = aop_test_str
 def __call__(self, func):
 @wraps(func)
 def wrapper(*args, **kwargs):
 print('before ' + self.aop_test_str)
 func()
 print('after ' + self.aop_test_str)
 return wrapper
 
@aop('pppppp')
def hi():
 print('hi')

看得出來,這個修飾器類也不過是多了個__call__函數,而這個__call__函數的內容和之前寫的修飾器函數一個樣!而使用這個修飾器的方法,和之前也一樣,一樣的如例子中的@aop('pppppp')。

過于無聊,還試了一下繼承的修飾器類:

class sub_aop(aop):
 def __init__(self, sub_aop_str, *args, **kwargs):
 self.sub_aop_str = sub_aop_str
 super(sub_aop, self).__init__(*args, **kwargs)
 def __call__(self, func):
 @wraps(func)
 def wrapper(*args, **kwargs):
 print('before ' + self.sub_aop_str)
 super(sub_aop, self).__call__(func)()
 print('after ' + self.sub_aop_str)
 return wrapper
 
@sub_aop('ssssss', 'pppppp')
def hello():
 print('hello')
 
if __name__ == '__main__':
 hello()

大家可以大膽猜測一下結果會怎樣。。。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

平潭县| 武川县| 志丹县| 西丰县| 长沙县| 阿拉善左旗| 阳泉市| 武胜县| 来凤县| 柏乡县| 商南县| 锦屏县| 黔西| 瓦房店市| 龙陵县| 错那县| 阿城市| 东源县| 郑州市| 宣化县| 西宁市| 汤阴县| 毕节市| 利津县| 镶黄旗| 贞丰县| 巴马| 日土县| 疏附县| 康定县| 金寨县| 汤原县| 慈溪市| 平遥县| 军事| 元江| 新河县| 绥阳县| 芦溪县| 延庆县| 甘南县|