10个美妙的Python装饰器
10個美妙的Python裝飾器 對Python編程語言中我最喜歡的一些裝飾器的概述。
簡介
關(guān)于Python編程語言的偉大之處在于,它在一個小包里裝了所有的功能,這些功能非常有用。很多特性可以完全改變Python代碼的功能,這使得該語言更加通用。此外,如果使用得當(dāng),這些功能中的一些可以起到縮短編寫有效軟件的時間的作用。Python的裝飾器是一個很好的例子,它很好地完成了這些目標(biāo)。
裝飾器是快速編程宏,可以用來改變 Python 對象的行為。它們可以應(yīng)用于類和函數(shù),并且實際上可以做很多非常有趣的事情。裝飾器可以用來縮短代碼,加快代碼速度,并完全改變代碼在Python中的行為方式。不用說,這肯定能派上用場! 今天我想展示一些我認為值得一看的裝飾器。有很多裝飾器,但我挑選了一些我認為功能最酷的。
№1: @lru_cache
這個列表中的第一個裝飾器來自functools模塊。這個模塊包含在標(biāo)準庫中,而且非常容易使用。除了這個裝飾器之外,它還包含了很多很酷的功能,但這個裝飾器無疑是我最喜歡的。
@lru_cache裝飾器可以用來加快函數(shù)和操作的連續(xù)運行速度,使用緩存。當(dāng)然,在使用時要注意交換和緩存的一些注意事項,但在通用的使用情況下,大多數(shù)時候這個裝飾器是值得使用的。如果你想了解更多關(guān)于Functools和我為什么喜歡它,實際上我寫了一整篇文章,你可以在這里閱讀。
FuncTools:一個被低估的Python包 用Functools把你的Python函數(shù)提升到一個新的水平!
能夠用一個簡單的裝飾器來加快代碼的速度是令人難以置信的。一個可以從這樣的裝飾器中受益的函數(shù)的很好的例子是遞歸函數(shù),比如這個計算階乘的函數(shù)。
def?factorial(n):????return?n?*?factorial(n-1)?if?n?else?1
遞歸對于計算時間來說是相當(dāng)困難的,但是添加這個裝飾器可以幫助大大加快這個函數(shù)的連續(xù)運行速度。
@lru_cachedef?factorial(n):
????return?n?*?factorial(n-1)?if?n?else?1
現(xiàn)在,每當(dāng)我們運行這個函數(shù)時,前幾個階乘的計算將被保存到緩存中。因此,下次我們再去調(diào)用這個函數(shù)時,只需要計算我們之前使用的階乘之后的階乘。當(dāng)然,并不是所有的階乘計算都會被保存,但是很容易看出為什么這是一個很好的應(yīng)用,這個裝飾器可以加速一些自然緩慢的代碼。
№2: @jit
JIT是Just In Time編譯的簡稱。通常我們在Python中運行一些代碼時,首先發(fā)生的是編譯。這種編譯產(chǎn)生了一些開銷,因為類型被分配了內(nèi)存,并以未分配但命名的別名的形式存儲。有了及時編譯,我們在執(zhí)行時就完成了大部分的工作。在很多方面,我們可以認為這類似于并行計算,Python 解釋器同時處理兩件事情,以節(jié)省一些時間。
Numba JIT編譯器就是因為在Python中提供了這個概念而聞名。與@lru_cache類似,這個裝飾器可以很容易地被調(diào)用,在你的代碼中可以立即提升性能。Numba包提供了jit裝飾器,這使得運行更密集的軟件變得更容易,而不需要落入C語言。
from?numba?import?jitimport?random
@jit(nopython=True)
def?monte_carlo_pi(nsamples):
????acc?=?0
????for?i?in?range(nsamples):
????????x?=?random.random()
????????y?=?random.random()
????????if?(x?**?2?+?y?**?2)?<?1.0:
????????????acc?+=?1
????return?4.0?*?acc?/?nsamples
№3: @do_twice
do_twice裝飾器的作用與它的名字差不多。這個裝飾器可以被用來在一次調(diào)用中運行一個函數(shù)兩次。這當(dāng)然有一些用途,我發(fā)現(xiàn)它對調(diào)試特別有幫助。它的另一個用途是測量兩個不同迭代的性能。以Functools為例,我們可以讓一個函數(shù)運行兩次,以便檢查出現(xiàn)的改進。這個函數(shù)是由Python中的裝飾器模塊提供的,它在標(biāo)準庫中。
from?decorators?import?do_twice@do_twice
def?timerfunc():
????%timeit?factorial(15)
№4: @count_calls
與do_twice裝飾器的簡單性相一致的是count_calls裝飾器。這個裝飾器可以用來提供一個函數(shù)在軟件中被使用多少次的信息。
和do_twice一樣,這在調(diào)試時肯定會派上用場。當(dāng)添加到一個給定的函數(shù)時,我們將收到一個輸出,告訴我們該函數(shù)在每次運行時被運行了多少次。這個裝飾器也在標(biāo)準庫的裝飾器模塊中。
from?decorators?import?count_calls@count_calls
def?function_example():
????print("Hello?World!"
function_example()
function_example()
function_example()
№5: @dataclass
為了在編寫類時節(jié)省時間,我一直利用的最好的裝飾器之一是數(shù)據(jù)類裝飾器。這個裝飾器可以用來快速編寫常見的標(biāo)準方法,這些方法通常在我們編寫的類中出現(xiàn)。如果你想進一步了解這個裝飾器,我也有一篇關(guān)于它的文章,你可以在這里閱讀。
這個裝飾器來自于dataclass模塊。這個模塊也在標(biāo)準庫中,所以沒有必要用PIP來嘗試這個例子
from?dataclasses?import?dataclass@dataclassclass?Food:
????name:?str
????unit_price:?float
????stock:?int?=?0
????????
????def?stock_value(self)?->?float:
????????return(self.stock?*?self.unit_price)
這段代碼將自動創(chuàng)建一個初始化函數(shù),__init__(),其中有必要的位置參數(shù)來填充我們類中的數(shù)據(jù)。它們也將自動提供給self,所以沒有必要為了在類中獲得一些數(shù)據(jù)參數(shù)而寫一個很長的函數(shù)。
№6: @singleton
為了理解 @singleton裝飾器的目的,我們首先需要理解什么是singleton。singleton在某種意義上是全局類型的一個版本。這意味著,這些類型被定義為只存在一次。
盡管這些類型在像 C++ 這樣的語言中很常見,但在 Python 中卻很少見到。對于singleton,我們創(chuàng)建一個只使用一次的類,并對該類進行突變,而不是初始化構(gòu)造類型。在這種情況下,類型的作用不像是模板,而更像是一個單一的受控對象。
通常情況下,singleton裝飾器是由用戶制作的,事實上并沒有導(dǎo)入。這是因為單子仍然是對我們的單子裝飾器中提供的模板的引用。我們可以命名一個單子函數(shù)并編寫一個包裝器,以便在我們的類上使用這個裝飾器。
def?singleton(cls):????instances?=?{}
????def?wrapper(*args,?**kwargs):
????????if?cls?not?in?instances:
??????????instances[cls]?=?cls(*args,?**kwargs)
????????return?instances[cls]
????return?wrapper
@singleton
class?cls:
????def?func(self):
另一種方法是通過使用元類來解決這個問題。如果你想了解更多關(guān)于元類的信息,我在去年寫了一篇文章,詳細介紹了這些元類和它們的用途,你可以在這里查看。
class?Singleton(type):????_instances?=?{}
????def?__call__(cls,?*args,?**kwargs):
????????if?cls?not?in?cls._instances:
????????????cls._instances[cls]?=?super(Singleton,?cls).__call__(*args,?**kwargs)
????????return?cls._instances[cls]
????????
class?Logger(object):
????__metaclass__?=?Singleton
№7: @use_unit
對于科學(xué)計算來說,有一個裝飾器可能經(jīng)常派上用場,那就是use_unit裝飾器。這個裝飾器可以用來改變返回方法的輸出。
這對于那些不想給他們的數(shù)據(jù)添加測量單位,但仍想讓人們知道這些單位是什么的人來說是很有用的。這個裝飾器在任何模塊中都不是真正可用的,但它是非常常用的,對科學(xué)應(yīng)用來說非常有用。
def?use_unit(unit):????"讓一個函數(shù)返回一個給定單位的數(shù)量"
????use_unit.ureg?=?pint.UnitRegistry()
????def?decorator_use_unit(func):
????????@functools.wraps(func)
????????def?wrapper_use_unit(*args,?**kwargs):
????????????value?=?func(*args,?**kwargs)
????????????return?value?*?use_unit.ureg(unit)
????????return?wrapper_use_unit
????return?decorator_use_unit
@use_unit("meters?per?second")
def?average_speed(distance,?duration):
????return?distance?/?duration
№7: @wraps
函數(shù)包裝器 @wraps是一種設(shè)計模式,用于處理Python中相對復(fù)雜的函數(shù)。封裝函數(shù)通常用來做一些可能被認為是更低級的、迭代的任務(wù)。封裝函數(shù)的好處是,它可以用來極大地提高性能。和lru_cache裝飾器一樣,這個裝飾器是由FuncTools包提供的。
import?functools?as?ftwraps裝飾器本身只是一個方便的裝飾器,用于更新一個給定函數(shù)的包裝器。當(dāng)這個裝飾器被調(diào)用時,該函數(shù)將在每次使用時更新其包裝器。這對提高性能有很大的幫助,FuncTools中的許多工具都是如此。
def?my_decorator(f):????@wraps(f)
????def?wrapper(*args,?**kwds):
????????print('Calling?decorated?function')
????????return?f(*args,?**kwds)
????return?wrapper
@wraps本身就是典型的裝飾器,wrapper裝飾器的調(diào)用通常用于該裝飾器調(diào)用中的一個包裝器函數(shù)。寫完這段代碼后,我們就可以用那個包裝器來裝飾我們的新函數(shù)了。
@my_decoratordef?func(x):
????print(x)???
??
№8: @staticmethod
在某些情況下,人們可能希望能夠訪問那些被私下定義的東西,就像在一個更全局的意義上。有時我們有一些包含在類中的函數(shù),我們希望將其方法化,而這正是staticmethod裝飾器的用途。
使用這個裝飾器,我們可以使C++靜態(tài)方法與我們的類一起工作。通常情況下,寫在類的范圍內(nèi)的方法對該類來說是私有的,除非作為子類調(diào)用,否則無法訪問。然而,在某些情況下,你可能希望采取一種更實用的方式來處理你的方法與數(shù)據(jù)的交互。使用這個裝飾器,我們可以創(chuàng)建這兩個選項,而不需要創(chuàng)建一個以上的函數(shù)。
class?Example:????@staticmethod
????def?our_func(stuff):
????????print(stuff)?????
我們也不需要明確地提供我們的類作為一個參數(shù)。staticmethod裝飾器為我們處理了這個問題。
№9: @singledispatch
FuncTools在這個列表中再次出擊,推出了非常有用的singledispatch裝飾器。單一調(diào)度是一種編程技術(shù),在許多編程語言中都很常見,因為它是一種很好的編程方式。雖然我更傾向于多路調(diào)度,但我認為單路調(diào)度在很多方面都可以發(fā)揮同樣的作用。
這個裝飾器使得在 Python 中處理類型要容易得多。當(dāng)我們在處理要通過同一個方法的多個類型時,情況更是如此。我在我的FuncTools文章中寫了更多關(guān)于這個問題的內(nèi)容,所以如果你對使用單一派發(fā)方法感興趣,我確實推薦你使用(鏈接在#1。)
@singledispatchdef?fun(arg,?verbose=False):
????????if?verbose:
????????????print("Let?me?just?say,",?end="?")
????????print(arg)
@fun.register
def?_(arg:?int,?verbose=False):
????if?verbose:
????????print("Strength?in?numbers,?eh?",?end="?")
????print(arg)
@fun.register
def?_(arg:?list,?verbose=False):
????if?verbose:
????????print("Enumerate?this:")
????for?i,?elem?in?enumerate(arg):
????????print(i,?elem)
№10: @register
注冊函數(shù)來自于模塊atexit。考慮到該模塊的名稱和工作原理,你可能會想到這個裝飾器可能與終止時執(zhí)行某些動作有關(guān)。
register裝飾器命名了一個終止時要運行的函數(shù)。例如,這將與一些需要在你退出時進行保存的軟件一起工作。
from?atexit?import?register@register
def?termin():
????print("?Goodbye!"?)
總結(jié)
不用說,Python 的裝飾器是非常有用的。它們不僅可以用來減慢編寫一些代碼的時間,而且對加快代碼的速度也有很大的幫助。當(dāng)你發(fā)現(xiàn)裝飾器的時候,不僅是令人難以置信的有用,而且編寫你自己的裝飾器也是一個好主意。
這些裝飾器之所以強大,是因為裝飾器作為一種特性在 Python 中非常強大。感謝你閱讀我的文章,我希望它能讓你注意到Python的一些很酷的能力!
本文由 mdnice 多平臺發(fā)布
總結(jié)
以上是生活随笔為你收集整理的10个美妙的Python装饰器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Total.Recorder.Edito
- 下一篇: 40套免费网站模板下载,可用于CMS建站