python裝飾器帶參數函數二階導數公式_一文搞定Python裝飾器,看完面試不再慌

本文始發于個人公眾號:TechFlow,原創不易,求個關注

今天是Python專題的第12篇文章,我們來看看Python裝飾器。

一段囧事

差不多五年前面試的時候,我就領教過它的重要性。那時候我Python剛剛初學乍練,看完了廖雪峰大神的博客,就去面試了。我應聘的并不是一個Python的開發崗位,但是JD當中寫到了需要熟悉Python。我看網上的面經說到Python經常會問裝飾器,我當時想的是裝飾器我已經看過了,應該問題不大……

沒想到面試的時候還真的問到了,面試官問我Python當中的裝飾器是什么。由于緊張和遺忘,我支支吾吾了半天也沒答上來。我隱約聽到了電話那頭的一聲嘆息……

時隔多年,我已經不記得那是一家什么公司了(估計規模也不大),但裝飾器很重要這個事情給我深深打下了烙印。

裝飾器本質

如今如果再有面試官問我Python中的裝飾器是什么,我一句話就能給回答了,倒不是我裝逼,實際上也的確只需要一句話。Python中的裝飾器,本質上就是一個高階函數。

你可能不太清楚高階函數的定義,沒關系,我們可以類比一下。在數學當中高階導數,比如二次導數,表示導數的導數。那么這里高階函數自然就是函數的函數,結合我們之前介紹過的函數式編程,也就是說是一個返回值是函數的函數。但是這個定義是充分不必要的,也就是說裝飾器是高階函數,但是高階函數并不都是裝飾器。裝飾器是高階函數一種特殊的用法。

任意參數

在介紹裝飾器的具體使用之前,我們先來了解和熟悉一下Python當中的任意參數。

Python當中支持任意參數,它寫成*args, **kw。表示的含義是接受任何形式的參數。

舉個例子,比如我們定義一個函數:

def exp(a, b, c='3', d='f'):

print(a, b, c, d)

我們可以這樣調用:

args = [1, 3]

dt = {'c': 4, 'd': 5}

exp(*args, **dt)

最后輸出的結果是1, 3, 4, 5。也就是說我們用一個list和dict可以表示任何參數。因為Python當中規定必選參數一定寫在可選參數的前面,而必選參數是可以不用加上名稱標識的,也就是可以不用寫a=1,直接傳入1即可。那么這些沒有名稱標識的必選參數就可以用一個list來表示,而可選參數是必須要加上名稱標識的,這些參數可以用dict來表示,這兩者相加可以表示任何形式的參數。

注意我們傳入list和dict的時候前面加上了*和**,它表示將list和dict當中的所有值展開。如果不加的話,list和dict會被當成是整體傳入。

所以如果一個函數寫成這樣,它表示可以接受任何形式的參數。

def exp(*args, **kw):

pass

定義裝飾器

明白了任意參數的寫法之后,裝飾器就不難了。

既然我們可以用*args, **kw接受任何參數。并且Python當中支持一個函數作為參數傳入另外一個函數,如果我們把函數和這個函數的所有參數全部傳入另外一個函數,那么不就可以實現代理了嗎?

還是剛才的例子,我們額外增加一個函數:

def exp(a, b, c='3', d='f'):

print(a, b, c, d)

def agent(func, *args, **kwargs):

func(*args, **kwargs)

args = [1]

dt = {'b': 1, 'c': 4, 'd': 5}

agent(exp, *args, **dt)

裝飾器的本質其實就是這樣一個agent函數,但是如果使用的時候需要手動傳入會非常麻煩,使用起來不太方便。所以Python當中提供了特定的庫,我們可以讓裝飾器以注解的方式使用,大大簡化操作:

from functools import wraps

def wrapexp(func):

def wrapper(*args, **kwargs):

print('this is a wrapper')

func(*args, **kwargs)

return wrapper

@wrapexp

def exp(a, b, c='3', d='f'):

print(a, b, c, d)

args = [1, 3]

dt = {'c': 4, 'd': 5}

exp(*args, **dt)

在這個例子當中,我們定義了一個wrapexp的裝飾器。我們在其中的wrapper方法當中實現了裝飾器的邏輯,wrapexp當中傳入的參數func是一個函數,wrapper當中的參數則是func的參數。所以我們在wrapper當中調用func(*args, **kw),就是調用打上了這個注解的函數本身。比如在這個例子當中,我們沒有做任何事情,只是在原樣調用之前多輸出了一行’this is a wrapper',表示我們的裝飾器調用成功了。

裝飾器用途

我們理解了裝飾器的基本使用方法之后,自然而然地會問一個天然的問題,學會了它究竟有什么用呢?

如果你從上面的例子當中沒有領會到裝飾器的強大,不如讓我用一個例子再來暗示一下。比如說你是一個程序員,辛辛苦苦做出了一個功能,寫了好幾千行代碼,上百個函數,終于通過了審核上線了。這個時候,你的產品經理找到了你說,經過分析我們發現上線的功能運行速度不達標,經常有請求超時,你能不能計算一下每個函數運行的耗時,方便我們找到需要優化的地方?

這是一個非常合理的請求,但想想看你寫了上百個函數,如果每一個函數都要手動添加時間計算,這要寫多少代碼?萬一哪個函數不小心改錯了,你又得一一檢查,并且如果要求嚴格的話你還得為每一個函數專門寫一個單元測試……

我想,正常的程序員應該都會抗拒這個需求。

但是有了裝飾器就很簡單了,我們可以實現一個計算函數耗時的裝飾器,然后我們只需要給每一個函數加上注解就好了。

import time

from functools import wraps

def timethis(func):

def wrapper(*args, **kwargs):

start = time.time()

result = func(*args, **kwargs)

end = time.time()

print(func.__name__, end-start)

return result

return wrapper

這也是裝飾器最大的用途,可以在不修改函數內部代碼的前提下,為它包裝一些額外的功能。

元信息

我們之前說過裝飾器的本質是高階函數,所以我們也可以和高階函數一樣來調用裝飾器,比如下面這樣:

def exp(a, b, c='3', d='f'):

print(a, b, c, d)

args = [1, 3]

dt = {'c': 4, 'd': 5}

f = wrapexp(exp)

f(*args, **dt)

這樣的方式得到的結果和使用注解是一樣的,也就是說我們加上注解的本質其實就是調用裝飾器返回一個新的函數。

既然和高階函數是一樣的,那么就帶來了一個問題,我們使用的其實已經不再是原函數了,而是一個由裝飾器返回的新函數,雖然這個函數的功能和原函數一樣,但是一些基礎的信息其實已經丟失了。

比如我們可以打印出函數的name來做個實驗:

正常的函數調用__name__返回的都是函數的名稱,但是當我們加上了裝飾器的注解之后,就會發生變化,同樣,我們輸出加上了裝飾器注解之后的結果:

我們會發現輸出的結果變成了wrapper,這是因為我們實現的裝飾器內部的函數叫做wrapper。不僅僅是__name__,函數內部還有很多其他的基本信息,比如記錄函數內描述的__doc__,__annotations__等等,這些基本信息被稱為是元信息,這些元信息由于我們使用注解發生了丟失。

有沒有什么辦法可以保留這些函數的元信息呢?

其實很簡單,Python當中為我們提供了一個專門的裝飾器用來保留函數的元信息,我們只需要在實現裝飾器的wrapper函數當中加上一個注解wraps即可。

def wrapexp(func):

@wraps(func)

def wrapper(*args, **kwargs):

print('this is a wrapper')

func(*args, **kwargs)

return wrapper

加上了這個注解之后,我們再來檢查函數的元信息,會發現它和我們預期一致了。

總結

了解了Python中的裝飾器之后,再來看之前我們用過的@property, @staticmethod等注解,想必都能明白,它們背后的實現其實也是裝飾器。靈活使用裝飾器可以大大簡化我們的代碼,讓我們的代碼更加規范簡潔,還能靈活地實現一些特殊的功能。

裝飾器的用法很多,今天介紹的只是其中最基本的,在后續的文章當中,還會繼續和大家分享它更多其他的用法。在文章開始的時候我也說了,裝飾器是Python進階必學的技能之一。想要熟練掌握這門語言,靈活運用,看懂大佬的源碼,裝飾器是必須會的東西。

希望大家都能有所收獲,原創不易,厚顏求個贊和關注~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/532059.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/532059.shtml
英文地址,請注明出處:http://en.pswp.cn/news/532059.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

centos7源碼安裝mysql報錯_CentOS7 下源碼安裝MySQL數據庫 8.0.11

本文主要向大家介紹了CentOS7 下源碼安裝MySQL數據庫 8.0.11,通過具體的內容向大家展現,希望對大家學習MySQL數據庫有所幫助。CentOS7 下源碼安裝MySQL 8.0.11系統環境:CentOS7, 內核:Linux 3.10.0-862.el7.x86_64如果…

python全排列問題_Python基于回溯法子集樹模板解決全排列問題示例

本文實例講述了Python基于回溯法子集樹模板解決全排列問題。分享給大家供大家參考,具體如下:問題實現 a, b, c, d 四個元素的全排列。分析這個問題可以直接套用排列樹模板。不過本文使用子集樹模板。分析如下:一個解x就是n個元素的一種排列&a…

file js new 傳到后臺_js 圖片上傳傳給后臺的3種格式

$("#imgfile").change(function () {var formData new FormData();$.each($(#imgfile)[0].files, function (i, file) {formData.set(idcard, file); //idcard 字段 根據自己后端接口定});//processData: false, contentType: false,多用來處理異步上傳二進制文件。…

usbserialcontroller驅動安裝不了_win10-有NVIDIA獨顯提示未安裝控制面板的離線安裝方式...

最近越來越多的用戶反映NVIDIA顯卡驅動設置不了啦,找不到NVIDIA顯卡的控制面板。 也不知道NVIDIA在什么版本開始驅動安裝包就不自帶NVIDIA顯卡控制面板了。 全新安裝的顯卡驅動就沒有控制面板;或者Windows 10自帶更新了顯卡新版驅動后導致沒有。 每次帶N…

mysql 多實例 獨立配置文件_三、安裝配置多實例MYSQL5.6-多獨立配置文件方法

三、安裝配置多實例MYSQL5.6-多獨立配置文件方法1、準備工作檢查操作系統版本、內核版本、selinux是否關閉、防火墻策略、IP地址、主機名配置、host表配置、yum配置上傳cmake、mysql5.6軟件包具體步驟參考源碼安裝mysql-單實例配置文檔2、安裝cmake軟件2.1 安裝編譯軟件環境[[e…

python做什么模型_主題模型初學者指南[Python]

引言近年來涌現出越來越多的非結構化數據,我們很難直接利用傳統的分析方法從這些數據中獲得信息。但是新技術的出現使得我們可以從這些輕易地解析非結構化數據,并提取出重要信息。主題模型是處理非結構化數據的一種常用方法,從名字中就可以看…

python實現隊列_Python學習教程:用隊列實現棧

接著上一期跟大家說的用棧實現隊列,這期的Python學習教程跟大家講用隊列實現棧題目:使用隊列實現棧的下列操作:push(x) – 元素 x 入棧pop() – 移除棧頂元素top() – 獲取棧頂元素empty() – 返回棧是否為空Implement the following operati…

vue 點擊li 中的img 怎么不冒泡_Vue全解

一.Vue實例內存圖:1.把Vue的實例命名為vm,vm對象封裝了對視圖的所有操作包括數據讀寫、事件綁定、DOM更新2.vm的構造函數是Vue,按照ES6的說法vm所屬的類是Vue3.options是new Vue的參數一般稱為選項或構造選項1.options里面有什么英文文檔搜op…

python布局管理_Python基礎=== Tkinter Grid布局管理器詳解

本文轉自:https://www.cnblogs.com/ruo-li-suo-yi/p/7425307.html 箬笠蓑衣Grid(網格)布局管理器會將控件放置到一個二維的表格里。主控件被分割成一系列的行和列,表格中的每個單元(cell)都可以放置一個控件。注意:不要試圖在一個主…

python面向對象類_python面向對象-類和對象

一. 類的定義class類名():代碼#定義類classWasher():defwash(self):print("洗衣服")注意:類名要滿足標識符命名規則,同時遵循大駝峰命名習慣。二. 創建對象對象名 類名()#創建對象w Washer()#調用方法w.wash() #洗衣服三. selfself指的是調用…

vant部署_vant ui rem配置流程

參考地址 https://www.cnblogs.com/WQLong/p/7798822.html1.下載lib-flexible使用的是vue-cliwebpack,通過npm來安裝的npm i lib-flexible --save2.引入lib-flexible在main.js中引入lib-flexibleimport ‘lib-flexible/flexible‘3.設置meta標簽通過meta標簽&#…

terminal services 找不到_電腦局域網中查看不到其他計算機或無法連接的解決辦法...

在辦公環境中,電腦經常需要打開網絡,進行一些文件共享的操作,但是有時會出現很多無法共享的情況,之前有一篇文章講過解決辦法,今天再來將一下具體無法共享的錯誤提示和相對應的處理方法,主要有以下幾種情況…

如何避免mysql回表查詢_mysql如何避免回表查詢

《迅猛定位低效SQL?》留了一個尾巴:select id,name where name‘shenjian‘select id,name,sexwhere name‘shenjian‘多查詢了一個屬性,為何檢索過程完全不同?什么是回表查詢?什么是索引覆蓋?如何實現索引…

python爬蟲開發數據庫設計入門經典_Python3實現的爬蟲爬取數據并存入mysql數據庫操作示例...

本文實例講述了Python3實現的爬蟲爬取數據并存入mysql數據庫操作。分享給大家供大家參考,具體如下:爬一個電腦客戶端的訂單。羅總推薦,抓包工具用的是HttpAnalyzerStdV7,與chrome自帶的F12類似。客戶端有接單大廳,羅列…

python中multiply函數_python中numpy庫內multiply()、dot()和 * 三種乘法運算的區別小計...

首先,導入函數包:import numpy as np1.np.multiply()函數:數組:(點對點)對應位置元素相乘矩陣:對應位置元素相乘示例:A np.array([[1,2],[3,4]])B np.array([[1,3],[2,4]])A_mat np.mat(A)B_mat np.mat(B)A_B_mult…

安裝python3.6.1_如何安裝python3.6.1/

如何在win7下安裝Python及配置1、首先,從搜索python官載適合自己電腦python版本。2標右擊桌面“計算機”擇打開菜單欄中的性”。3、WindowsXP時,在新彈出的屬性窗口,選擇“高級”->“環境變量”。Windows7是,在新彈出的屬性窗口…

編程入門python java和c語言_學習編程適不適合從Python入門?哪種語言更適合入門?...

本文對比了C語言和Python語言,分析它們作為編程入門語言各自的利弊,并給出了我推薦的編程學習道路。我本身已經入門了Python腳本語言,在進階C語言和JAVA語言后,Python重學就輕松很多,幾個小時就拾起了忘記的語法&#…

mysql 備份 一張表_mysql 備份表的一個方法

#--- start# 新建表create table sp2_match_comment_tmp like sp2_match_comment; # 這種方式 外鍵索引,觸發器不會在新表中有,要自己添加LOCK TABLES sp2_match_comment write, sp2_match_comment AS smc2 read, sp2_match_comment_tmp write;# 導出最新…

springmvc的工作原理_SpringMVC工作原理

1 簡介SpringMVC框架是以請求為驅動,圍繞Servlet設計,將請求發給控制器,然后通過模型對象,分派器來展示請求結果視圖。其中核心類是DispatcherServlet,它是一個Servlet,頂層是實現的Servlet接口。2 運行原理…

java邏輯運算符_Java邏輯運算符

Java邏輯運算符Java邏輯運算符包含下面6中符號:&& 與 ;&& 與 前后兩個操作數必須都是true才返回true,否則返回false& 不短路與 ; & 不短路與 表達式都會執行到|| 或; || 或 只要兩個操作數中有一個是tru…