python裝飾器探究與參數的領取

来源:http://www.cnblogs.com/ansver/archive/2017/11/30/7900804.html
-Advertisement-
Play Games

首先上原文, 現在,假設我們要增強now()函數的功能,比如,在函數調用前後自動列印日誌,但又不希望修改now()函數的定義,這種在代碼運行期間動態增加功能的方式,稱之為“裝飾器”(Decorator)。本質上,decorator就是一個返回函數的高階函數。 Decorator本質是高階函數? 不信 ...


首先上原文,

現在,假設我們要增強now()函數的功能,比如,在函數調用前後自動列印日誌,但又不希望修改now()函數的定義,這種在代碼運行期間動態增加功能的方式,稱之為“裝飾器”(Decorator)。
本質上,decorator就是一個返回函數的高階函數。

Decorator本質是高階函數?

不信邪的我試了下..

 1 def g():
 2     print("這裡是G")
 3     return "G"
 4 
 5 @g
 6 def f():
 7     print("這裡是F")
 8     return 1
 9 '''
10 --------------------------------------------
11 line 5, in <module>
12     @g
13 TypeError: g() takes 0 positional arguments but 1 was given
14 >>>
15 '''

運行結果在註釋里

尷尬了...g被強制塞了個參數,那個參數應該是g"修飾的對象"

修改下繼續.....

def g(f):
    print("這裡是G")
    return "G"

@g
def f():
    print("這裡是F")
    return 1
'''
--------------------------------------------
這裡是G
>>> f()
Traceback (most recent call last):
  File "<pyshell#0>", line 1, in <module>
    f()
TypeError: 'str' object is not callable
'''

str對象不能被調用,,,這裡的str只有一個,就是g的返回值

為了驗證下,我把"G"改成了2

結果是

TypeError: 'int' object is not callable

OK了,大概意思就是,裝飾器首先被"塞"一個參數,然後,返回值還要被調用一次,然而好像只有函數才能被調用,,所以,為了不報錯,裝飾器必須返回一個函數,裝飾器必須是高階函數......

我表示不服( ̄へ ̄),不就是函數嗎,g的參數就是函數,,,騷操作上腦ing

def g(f):
    print("這裡是G")
    return f

@g
def f():
    print("這裡是F")
    return 1
'''
--------------------------------------------
這裡是G
>>> f()
這裡是F
'1'
'''

運行成功.但是....說好的"列印日誌功能"呢,#沒錯"這裡是G"就是我想要的日誌

疑點: 列印的"這裡是G"是第一行,是在輸入"f()"之前發生的....

#單看g函數,它不算是"高階函數"

作為成功的例子,它太失敗了┐( ̄ヘ ̄)┌

 

##好吧,我投降了,負隅頑抗也不怎麼有意思.....

閉包告訴我們一個道理,,,為了保證返回值一定是函數,最好的措施就是"在函數內部,現場造一個函數然後扔出去"

def g(f):
    print("這裡是G")
    def h():
        print('這裡是H')
        return "H"
    return h

@g
def f():
    print("這裡是F")
    return 1
'''
--------------------------------------------
這裡是G
>>> f()
這裡是H
'H'
>>> f()
這裡是H
'H'
>>> 
'''

f函數不執行了,,是的沒錯,我還多試了一次的

另外,兩條日誌只有裡面的能用......(下文會解釋的)

看了一下書,h函數返回f()的話,f函數就能被執行了,,,,,個人感覺裝飾器應該叫"劫持器"

def g(f):
    print("這裡是G")
    def h():
        print('這裡是H')
        return f()
    return h

@g
def f():
    print("這裡是F")
    return 1
'''
--------------------------------------------
這裡是G
>>> f()
這裡是H
這裡是F
'1'
>>> f
<function g.<locals>.h at 0x0000020CBDBB6C80>
'''

 

按書上的思路解釋下

'''
@g
def f():
    pass
>>>f()
等價於
>>>g(f) ()
g函數執行,返回
>>>h ()
h函數執行(列印日誌)
>>>f()
f執行,返回1
>>>1
'''

加上參數,

def g(f):
    print("這裡是G")
    def h(*args,**kw):
        print('這裡是H')
        return f(*args,**kw)
    return h

@g
def f(*args,**kw):
    print("這裡是F")
    return 1
'''
>>>f(*args,**kw)
等價於
>>>g(f) (*args,**kw)
g函數執行,返回
>>>h (*args,**kw)
h函數執行(列印日誌)
>>>f(*args,**kw)
f執行,返回1
>>>1
'''

可以看出,(*args,**kw)本改被h函數拿走,所以,觀察h函數,h把它的參數原封不動的交給了f

機智的我動了歪主意

def g(f):
    print("這裡是G")
    def h():#h沒要求參數
        print('這裡是H')
        return f
    return h

@g
def f(*args,**kw):
    print("這裡是F")
    return 1
'''
>>>f()(*args,**kw)
等價於
>>>g(f) ()(*args,**kw)
g函數執行,返回
>>>h ()(*args,**kw)
h函數執行,h拿的空參數
(列印日誌)
>>>f(*args,**kw)
f執行,返回1
>>>1
'''

可是新的問題來了,後面f調用的時候得多加個空括弧,否則

'''
>>>f(*args,**kw)
等價於
>>>g(f) (*args,**kw)
g函數執行,返回
>>>h (*args,**kw)
h函數執行(列印日誌)返回f
>>>f
這是一個函數對象
'''

以上告訴我們一個道理"函數執行不執行取決於後面有沒有括弧"

舉個例子

def m(a):
    print(a)
    return m

print(m(1)(2)(3)(4)(5)(6)(7)(8)(9)(10))
'''
-----------------------------
1
2
3
4
5
6
7
8
9
10
<function m at 0x000002832BDB10D0>

 

燒腦時刻

 

 f = a.b.c()()[0]()[d()()[e]]
a模塊的b類的c方法是高階函數,最終返回一個列表,列表裡有個函數
函數又返回一個字典............

下麵開始,,裝飾器的三層嵌套

它要解決的問題是裝飾器需要額外的參數怎麼辦

就是g(m)變成了g(canshu)(m)多了一個括弧,那麼就得多加一層函數,,否則就會出錯

>>> 1()
    1()
TypeError: 'int' object is not callable

.修改後的

def i(canshu):
    def g(f):
        print("這裡是G")
        def h(*args,**kw):
            print('這裡是H')
            return f(*args,**kw)
        return h
    return g

@i('canshu')
def f(*args,**kw):
    print("這裡是F")
    return 1
'''
--------------------------------------------
這裡是G
>>> f()
這裡是H
這裡是F
'1'
>>> f
<function i.<locals>.g.<locals>.h at 0x00000283A6886D08>
'''

解釋一下

'''
>>>f(*args,**kw)
等價於
>>>i('canshu')(f)(*args,**kw)
i函數返回g
>>>g(f)(*args,**kw)
g函數執行,返回h
>>>h(*args,**kw)
h函數執行(列印日誌)返回f(*args,**kw)
>>>f(*args,**kw)
f執行返回1


>>>f
等價於
>>>i('canshu')(f)
i函數返回g
>>>g(f)
g函數執行,返回h
>>>h
這是個函數對象
'''

所以

>>> f.__name__
'h'

真是劫持器呀,__name__都變了...

至於為什麼每次最開始都有一個

應該可以這樣理解

執行@i('canshu')語句時候裝飾器被執行了一次

'''
>>>i('canshu')(f)
i函數返回g
>>>g(f)
g函數執行,列印了一句,返回h
>>>h
函數對象
'''

驗證

def i(canshu):
    def g(f):
        print(f.__name__)
        print('這裡是G')
        def h(*args,**kw):
            print('這裡是H')
            return f(*args,**kw)
        return h
    return g

@i('canshu')
def f(*args,**kw):
    print("這裡是F")
    return 1
'''
--------------------------------------------
f
這裡是G
>>> f()
這裡是H
這裡是F
'1'
'''

綜上,新啟示

  1. 裝飾器只有最內層的內容能起"日誌"的作用,其它層的內容會在定義時執行
  2. 只要設計得當,一個函數後面可以跟好多括弧,函數層數和括弧數得儘量一致

 另外,"劫持器"還可以用於調試,當兩個函數不知道哪個更好時

def i(f_1):
    def g(f):
        def h(*args,**kw):
            print('%s把%s替換了'%(f_1.__name__,f.__name__))
            return f_1(*args,**kw)
        return h
    return g

def f_1(*args,**kw):
    print("這裡是F_1")
    return 2

@i(f_1)
def f(*args,**kw):
    print("這裡是F")
    return 1
'''
--------------------------------------------
>>> f()
f_1把f替換了
這裡是F_1
2
'''

f=f_1語句的加日誌版φ(>ω<*) 

 應該完了,,,

有什麼遺漏以後再補

 突然覺得這篇隨筆處於"小白看不懂,大神不屑一顧"的尷尬境地.....

.


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 第一階段:三年 我認為三年對於程式員來說是第一個門檻,這個階段將會淘汰掉一批不適合寫代碼的人。這一階段,我們走出校園,邁入社會,成為一名程式員,正式從書本 上的內容邁向真正的企業級開發。我們知道如何團隊協作、如何使用項目管理工具、項目版本如何控制、我們寫的代碼如何測試如何線上上運行等等,積累了一定的 ...
  • 1> 打開main.go文件,查看代碼 點擊快捷鍵F12,進入beego.go文件,查看代碼 2> 打開router.go文件,查看路由 3> 打開default.go文件,查看業務邏輯 4> 打開index.tpl文件,查看前臺頁面 5> 執行邏輯總結 第一步,進入main.go,編譯程式;第二步 ...
  • 本文介紹一下matplotlib的最基本用法。 這次我們要顯示一個線性方程的直線。 首先要引入matplotlib庫,一般是用plt這個簡寫的,我們就按照大多數人的慣例來進行命名: 下麵我們用numpy生成x軸上的數據:從 1到1,總共有50個點: 再定義一個線性方程: 接著把x和y在坐標軸上顯示出 ...
  • 英語里final這個單詞大家都知道是“最終的”意思,其實還有一個意思是“不可更改的”。在Java里,final關鍵字作“不可更改的”來解釋更合適,即由final修飾的東西是“不可更改的”。final可以修飾變數、成員方法和類,還可以修飾方法的參數,但參數歸根結底還是變數。下麵是詳細解釋。 作者: 蟬 ...
  • 一、回顧jQuery實現的ajax 首先說一下ajax的優缺點 jquery 實現的ajax 1 <!DOCTYPE html> 2 3 <html lang="en"> 4 <head> 5 <meta charset="UTF-8"> 6 <title>Title</title> 7 <scri ...
  • PyQt5 的 signal 與 slot 有所改變,例如,先定義一個 ZeroSignal 類: 使用時,一是綁定 slot 如下: 然後是找個機會發動之: 大約如此,完整代碼如下: OK! ...
  • java只所以被推廣,實際上很大原因是因為本身是跨平臺的,很大作用是因為虛擬機的關係。 一般情況下開發人員不需要關註虛擬機內部實現就可以日常開發了,但是有時候涉及到性能的時候就需要瞭解虛擬機的實現機制了。 那麼今天寫的內容更多的是關於編譯一套自己的虛擬機,為日後瞭解虛擬機底層原理鋪鋪路。 編譯虛擬機 ...
  • 歡迎訪問我的個人博客: "原文鏈接" 前言 人生苦短,我用python。學習python怎麼能不搞一下詞雲呢是不是(ง •̀_•́)ง 於是便有了這篇邊實踐邊記錄的筆記。 環境:VMware 12pro + CentOS7 + Python 2.7.5 安裝系統 之前一直用的是win10子系統,現在 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...