1. 程式人生 > >Python高階特性(2):Closures、Decorators和functools

Python高階特性(2):Closures、Decorators和functools

裝飾器(Decorators)

裝飾器是這樣一種設計模式:如果一個類希望新增其他類的一些功能,而不希望通過繼承或是直接修改原始碼實現,那麼可以使用裝飾器模式。簡單來說Python中的裝飾器就是指某些函式或其他可呼叫物件,以函式或類作為可選輸入引數,然後返回函式或類的形式。通過這個在Python2.6版本中被新加入的特性可以用來實現裝飾器設計模式。

順便提一句,在繼續閱讀之前,如果你對Python中的閉包(Closure)概念不清楚,請檢視本文結尾後的附錄,如果沒有閉包的相關概念,很難恰當的理解Python中的裝飾器。

在Python中,裝飾器被用於用@語法糖修辭的函式或類。現在讓我們用一個簡單的裝飾器例子來演示如何做一個函式呼叫日誌記錄器。在這個例子中,裝飾器將時間格式作為輸入引數,在呼叫被這個裝飾器裝飾的函式時打印出函式呼叫的時間。這個裝飾器當你需要手動比較兩個不同演算法或實現的效率時很有用。

12345678910111213141516171819 def logged(time_format):def decorator(func):def decorated_func(*args,**kwargs):print"-Running'%s' on %s " % (func.__name__,time.strftime(time_format))start_time=time.time()result=func(*args,**kwargs
)end_time=time.time()print"-Finished'%s', execution time = %0.3fs " % (func.__name__,end_time-start_time)returnresultdecorated_func.__name__=func.__name__returndecorated_funcreturndecorator

來看一個例子,在這裡add1和add2函式被logged修飾,下面給出了一個輸出示例。請注意在這裡時間格式引數是儲存在被返回的裝飾器函式中(decorated_func)。這就是為什麼理解閉包對於理解裝飾器來說很重要的原因。同樣也請注意返回函式的名字是如何被替換為原函式名的,以防萬一如果它還要被使用到,這是為了防止混淆。Python預設可不會這麼做。

1234567891011121314151617181920 @logged("%b%d%Y-%H:%M:%S")def add1(x,y):time.sleep(1)returnx+y@logged("%b%d%Y-%H:%M:%S")def add2(x,y):time.sleep(2)returnx+yprint add1(1,2)print add2(1,2)# Output:-Running'add1' on Jul 24 2013 - 13:40:47-Finished'add1', execution time = 1.001s3-Running'add2' on Jul 24 2013 - 13:40:48-Finished'add2', execution time = 2.001s3

如果你足夠細心,你可能會注意到我們對於返回函式的名字__name__有著特別的處理,但對其他的注入__doc__或是__module__則沒有如此。所以如果,在這個例子中add函式有一個doc字串的話,它就會被丟棄。那麼該如何處理呢?我們當然可以像處理__name__那樣對待所有的欄位,不過如果在每個裝飾器內都這麼做的話未免太繁冗了。這就是為何functools模組提供了一個名為wraps的裝飾器的原因,那正是為了處理這種情況。可能在理解裝飾器的過程中會被迷惑,不過當你把裝飾器看成是一個接收函式名作為輸入引數並且返回一個函式,這樣就很好理解了。我們將在下個例子中使用wraps裝飾器而不是手動去處理__name__或其他屬性。

下個例子會有點複雜,我們的任務是將一個函式呼叫的返回結果快取一段時間,輸入引數決定快取時間。傳遞給函式的輸入引數必須是可雜湊的物件,因為我們使用包含呼叫輸入引數的tuple作為第一個引數,第二個引數則為一個frozenset物件,它包含了關鍵詞項kwargs,並且作為cache key。每個函式都會有一個唯一的cache字典儲存在函式的閉包內。

【譯註】set和frozenset為Python的兩種內建集合,其中前者為可變物件(mutable),其元素可以使用add()或remove()進行變更,而後者為不可變物件(imutable)並且是可雜湊的(hashable),在建立之後元素不可變,他可以作為字典的key或是另一個集合的元素。

12345678910111213141516171819202122232425262728293031323334353637383940 import timefrom functools import wrapsdef cached(timeout,logged=False):"""Decorator tocache the result ofafunctioncall.Cache expires after timeout seconds."""def decorator(func):iflogged:print"--Initializing cache for",func.__name__cache={}@wraps(func)def decorated_function(*args,**kwargs):iflogged:print"--Called function",func.__name__key=(args,frozenset(kwargs.items()))result=Noneifkey incache:iflogged:print"--Cache hit for",func.__name__,key(cache_hit,expiry)=cache[key]iftime.time()-expiry<timeout:result=cache_hitelif logged:print"--Cache expired for",func.__name__,keyelif logged:print"--Cache miss for",func.__name__,key# No cache hit, or expiredifresult isNone:result=func(*args,**kwargs)cache[key]=(result,time.time())returnresultreturndecorated_functionreturndecorator

來看看它的用法。我們使用裝飾器裝飾一個很基本的斐波拉契數生成器。這個cache裝飾器將對程式碼使用備忘錄模式(Memoize Pattern)。請注意fib函式的閉包是如何存放cache字典、一個指向原fib函式的引用、logged引數的值以及timeout引數的最後值的。dump_closure將在文末定義。

12345678910111213141516171819202122232425262728293031 >>>@cached(10,True)...def fib(n):..."""Returns then'th Fibonacci number."""...ifn==0orn==1:...return1...returnfib(n-1)+fib(n-2)...--Initializing cache forfib>>>dump_closure(fib)1.Dumping functionclosure forfib:--cell0={}--cell1=--cell2=True--cell3=10>>>>>>print"Testing-F(4)=%d"%fib(4)--Called functionfib--Cache miss forfib((4,),frozenset([]))--Called functionfib--Cache miss forfib((3,),frozenset([]))--Called functionfib--Cache miss forfib((2,),frozenset([]))--Called functionfib--Cache miss forfib((1,),frozenset([]))--Called functionfib--Cache miss forfib((0,),frozenset([]))--Called functionfib--Cache hit forfib((1,),frozenset([]))--Called functionfib--Cache hit forfib((2,),frozenset([]))Testing-F(4)=5

Class Decorators

在之前的小節中,我們看了一些函式裝飾器和一些使用的小技巧,接下來我們來看看類裝飾器。類裝飾器將一個class作為輸入引數(Python中的一種類型別物件),並且返回一個修改過的class。

第一個例子是一個簡單的數學問題。當給定一個有序集合P,我們定義PdP的反序集合P(x,y) <-> Pd(x,y),也就是說兩個有序集合的元素順序互為相反的,這在Python中該如何實現?假定一個類定義了__lt__以及__le__或其他方法來實現有序。那麼我們可以通過寫一個類裝飾器來替換這些方法。

123456789101112 def make_dual(relation):@wraps(relation,[&#039;__name__&#039;, &#039;__doc__&#039;])def dual(x,y):returnrelation(y,x)returndualdef dual_ordering(cls):&quot;&quot;&quot;Classdecorator that reverses all the orderings&quot;&quot;&quot;forfunc in[&#039;__lt__&#039;, &#039;__gt__&#039;, &#039;__ge__&#039;, &#039;__le__&#039;]:ifhasattr(cls,func):setattr(cls,func,make_dual(getattr(cls,func)))returncls

下面是將這個裝飾器用以str型別的例子,建立一個名為rstr的新類,使用反字典序(opposite lexicographic)為其順序。

Python
12345678

相關推薦

Python高階特性2ClosuresDecoratorsfunctools

裝飾器(Decorators) 裝飾器是這樣一種設計模式:如果一個類希望新增其他類的一些功能,而不希望通過繼承或是直接修改原始碼實現,那麼可以使用裝飾器模式。簡單來說Python中的裝飾器就是指某些函式或其他可呼叫物件,以函式或類作為可選輸入引數,然後返回函式或類

Python高階特性1IteratorsGeneratorsitertools

【譯註】:作為一門動態指令碼語言,Python對程式設計初學者而言很友好,豐富的第三方庫能夠給使用者帶來很大的便利。而Python同時也能夠提供一些高階的特性方便使用者使用更為複雜的資料結構。本系列文章共有三篇,本文是系列的第一篇,將會介紹迭代器、生成器以及iter

python每日一類2platform

獲得 min def glob model implement 匯總 uname 信息 根據官方文檔的解釋(https://docs.python.org/3.5/library/platform.html#module-platform): 學習其他人的代碼如下: #

python函數2函數進階

int splay 基本 源文件 tuple [0 執行 內容 理念 昨天說了函數的一些最基本的定義,今天我們繼續研究函數。今天主要研究的是函數的命名空間、作用域、函數名的本質、閉包等等 預習: 1、寫函數,用戶傳入修改的文件名,與要修改的內容,執行函數,完成整個文件

Python開發環境2啟動Eclipse時檢測到PYTHONPATH發生改變

工程 新的 “.” ase 窗口 什麽 程序 apply pri OS:Windows 10家庭中文版,Eclipse:Oxygen.1a Release (4.7.1a),PyDev:6.3.2 4月25日,在Eclipse上安裝了PyDev(前面博文有記錄),並開發

Python基礎2__doc__文檔字符串docStringhelp()

png Go net dex django index csdn 自定義模塊 pan OS:Windows 10家庭中文版,Python:3.6.4 Python中的 文檔字符串(docString) 出現在 模塊、函數、類 的第一行,用於對這些程序進行說明。它在執行的

Python設計模式2創建型

reg target ice cti lov term concrete The 延遲 1. Factory Method(工廠方法) 意圖:定義一個用於創建對象的接口,讓子類決定實例化哪一個類。Factory Method使一個類的實例化延遲到其子類。 適用性: 當一

python筆記----matplotlib2條形圖散點圖

#coding=utf-8 import pandas as pd import numpy as np import matplotlib.pyplot as plt#條形圖 reviews = pd.read_csv("fandango_scores.csv") col

C# 7.0 新特性2 本地方法

本文參考Roslyn專案中的Issue:#259. 簡而言之,【本地方法】就是在方法體內部定義一個方法。 其實咋眼一看,這個新特新並沒有什麼新意,因為目前大量C#的專案中,都可以使用delegate或基於delegate變形的各種方案(lambda, Fun,

Python學習筆記2生成隨機數

Python中的random模組提供了一些很有用的方法來幫我們生成隨機數。接下來我們來看一下random模組中都包括哪些方法。 randint randint方法接收兩個整型引數a和b,其中第一個引數必須小於第二個引數(可以等於,但是沒有意義)。然後返回

Python高階特性3: ClassesMetaclasses

類和物件 類和函式一樣都是Python中的物件。當一個類定義完成之後,Python將建立一個“類物件”並將其賦值給一個同名變數。類是type型別的物件(是不是有點拗口?)。 類物件是可呼叫的(callable,實現了 __call__方法),並且呼叫它能夠建立類

SQL Server2016新特性2時態表 Temporal Table

SQL Server 2016 引入了對版本由系統控制的臨時表的支援,其附帶的內建支援可以提供表中儲存的資料在任意時間點的相關資訊,而不僅僅是資料在當前時刻正確的資訊。 臨時表是 ANSI SQL 2011 中引入的資料庫功能。 版本由系統控制的臨時表是使用者表的一種型別,

python高階特性1:切片

原文 取一個list或tuple的部分元素是非常常見的操作。比如,一個list如下: L = [‘Michael’, ‘Sarah’, ‘Tracy’, ‘Bob’, ‘Jack’] 取前3個元素,應該怎麼做?

Python爬蟲實戰2爬取京東商品列表

1,引言在上一篇》,爬取了一個用Drupal做的論壇,是靜態頁面,抓取比較容易,即使直接解析html原始檔都可以抓取到需要的內容。相反,JavaScript實現的動態網頁內容,無法從html原始碼抓取

Python高階爬蟲動態載入頁面的解決方案與爬蟲代理

*原創作者:VillanCh 0×00 前言 0×01 動態頁面解決方案Selenium+PhantomJS 0×02 原理回顧對比 0×03 Quick Start 0×04 webdriver API 0×05 匿名爬蟲 0×06 匿名爬蟲解決方案 0×07 完結總結

小白學 Python 資料分析2Pandas 概述

人生苦短,我用 Python 前文傳送門: 小白學 Python 資料分析(1):資料分析基礎 概覽 首先還是幾個官方連結放一下: Pandas 官網:https://pandas.pydata.org/ Pandas 中文網:https://www.pypandas.cn/ Pandas Githu

《Linux學習並不難》Linux網絡命令2ifconfig命令顯示配置網絡接口

Linux ifconfig 27.2 《Linux學習並不難》Linux網絡命令(2):ifconfig命令顯示和配置網絡接口使用ifconfig命令可以顯示和配置網絡接口,比如設置IP地址、MAC地址、激活或關閉網絡接口。命令語法:ifconfig [接口] [選項| IP地址]命令中各選項的含

Python學習筆記4容器叠代對象叠代器生成器生成器表達式

iter 有一種 ref function 但是 tool 數列 edt 叠代器類型 在了解Python的數據結構時,容器(container)、可叠代對象(iterable)、叠代器(iterator)、生成器(generator)、列表/集合/字典推導式(list,se

Qt 學習之路 242QListWidgetQTreeWidget QTableWidget

上一章我們瞭解了 model/view 架構的基本概念。現在我們從最簡單的QListWidget、QTreeWidget和QTableWidget三個類開始瞭解最簡單的 model/view 的使用。這部分內容的確很難組織。首先,從最標準的 model/view 開始,往往會糾結於複雜的程式碼;但是

Docker學習2Docker映象介紹容器的兩種建立方式

1.什麼是Docker映象     Docker映象是啟動容器構建的基石,是由檔案系統疊加而成,最底端是一個引導檔案系統,即bootfs,這很像典型的Linux的引導檔案系統,但是Docker使用者幾