python函數(shù)修飾符@ 修飾符 ‘@’符號用作函數(shù)修飾符是python2.4新增加的功能,修飾符必須出現(xiàn)在函數(shù)定義前一行,不允許和函數(shù)定義在同一行。也就是說@A def f(): 是非法的。 只可以在模塊或類定義層內(nèi)對函數(shù)進行修飾,不允許修修飾一個類。一個修飾符就是一個函數(shù),它將被修飾的函數(shù)做為參數(shù),并返回修飾后的同名函數(shù)或其它可調(diào)用的東西。 本質(zhì)上講,裝飾符@類似于 回調(diào)函數(shù) ,把其它的函數(shù)(暫且稱為目的參數(shù),后面緊接著的函數(shù))作為自己的入?yún)?,在目的函?shù)執(zhí)行前,執(zhí)行一些自己的操作, 比如:計數(shù)、打印一些提示信息等,然后返回目的函數(shù)。下面列舉一個簡單的例子。
讓客戶滿意是我們工作的目標,不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價值的長期合作伙伴,公司提供的服務(wù)項目有:域名注冊、網(wǎng)頁空間、營銷軟件、網(wǎng)站建設(shè)、七星網(wǎng)站維護、網(wǎng)站推廣。
創(chuàng)建函數(shù)修飾符的規(guī)則:
(1)修飾符是一個函數(shù)
(2)修飾符取被修飾函數(shù)為參數(shù)
(3)修飾符返回一個新函數(shù)
(4)修飾符維護被維護函數(shù)的簽名
例子1: 被修飾函數(shù)不帶參數(shù)
運行結(jié)果:
例子2: 使用functools模塊提供的修改函數(shù)屬性的方法wraps
運行結(jié)果:
可見test1的函數(shù)名稱變了,如果某些代碼用到就會出問題,可以使用functools模塊提供的修改函數(shù)屬性的方法wraps
運行結(jié)果:
例子3: 被修飾函數(shù)帶參數(shù)
運行結(jié)果:
例子4: 修飾符帶參數(shù) ,需要比上面例子多一層包裝
運行結(jié)果:
藍海大腦深度學(xué)習高性能計算液冷事業(yè)部研究人員表示:對于 Python 來說,“它的靈活性和無類型的高級語法可能會導(dǎo)致數(shù)據(jù)和計算密集型程序的性能不佳,因為運行本地編譯代碼要比運行動態(tài)解釋代碼快很多倍。因此,注重效率的 Python 程序員通常會使用 C 語言重寫最內(nèi)層的循環(huán),然后從 Python 調(diào)用已編譯的 C 語言函數(shù)。許多項目都力求簡化這種優(yōu)化(例如 Cython),但它們通常需要學(xué)習新的語法。雖然 Cython 顯著提高了性能,但可能需要對 Python 代碼進行艱巨的手動修改工作。
Numba 被視作 Cython 的替代方案,并且要簡單得多。它最大的吸引力在于無需學(xué)習新的語法,也無需替換 Python 解釋器、運行單獨的編譯步驟或安裝 C/C++ 編譯器。只需將 @jit Numba 修飾器應(yīng)用于 Python 函數(shù)即可。這樣,在運行時即可進行編譯(即“即時”或 JIT 編譯)。Numba 能夠動態(tài)編譯代碼,這意味著,您還可以享受 Python 帶來的靈活性。此外,Python 程序中由 Numba 編譯的數(shù)值算法,可以接近使用編譯后的 C 語言或 FORTRAN 語言編寫的程序的速度;并且與原生 Python 解釋器執(zhí)行的相同程序相比,運行速度最多快 100 倍。這是一項重要進步,推動了高效編程與高性能計算的完美結(jié)合。
Numba 專為面向數(shù)組的計算任務(wù)而設(shè)計,與應(yīng)用廣泛的 NumPy 庫類似。在面向數(shù)組的計算任務(wù)中,數(shù)據(jù)并行性與 GPU 等加速器自然契合。Numba 理解 NumPy 數(shù)組類型,并將其用于生成高效的編譯代碼,以在 GPU 或多核 CPU 上執(zhí)行。所需的編程工作非常簡單,只需添加一個 @vectorize 函數(shù)修飾器,指示 Numba 在運行時生成編譯的向量化函數(shù)版本。這樣,它便可用于在 GPU 上并行處理數(shù)據(jù)數(shù)組了。
你裝飾器的用法不對,你要的功能大概的寫法如下:
====
def?tsfun(func,?*args,?**kwargs):
print?"%s,%s,?called"?%(ctime(),func.__name__)
func(*args,?**kwargs)
先來個形象比方
內(nèi)褲可以用來遮羞,但是到了冬天它沒法為我們防風御寒,聰明的人們發(fā)明了長褲,有了長褲后寶寶再也不冷了,裝飾器就像我們這里說的長褲,在不影響內(nèi)褲作用的前提下,給我們的身子提供了保暖的功效。
再回到我們的主題
裝飾器本質(zhì)上是一個Python函數(shù),它可以讓其他函數(shù)在不需要做任何代碼變動的前提下增加額外功能,裝飾器的返回值也是一個函數(shù)對象。它經(jīng)常用于有切面需求的場景,比如:插入日志、性能測試、事務(wù)處理、緩存、權(quán)限校驗等場景。裝飾器是解決這類問題的絕佳設(shè)計,有了裝飾器,我們就可以抽離出大量與函數(shù)功能本身無關(guān)的雷同代碼并繼續(xù)重用。概括的講,裝飾器的作用就是為已經(jīng)存在的對象添加額外的功能。
先來看一個簡單例子:
def foo():
print('i am foo')
現(xiàn)在有一個新的需求,希望可以記錄下函數(shù)的執(zhí)行日志,于是在代碼中添加日志代碼:
def foo():
print('i am foo')
logging.info("foo is running")
bar()、bar2()也有類似的需求,怎么做?再寫一個logging在bar函數(shù)里?這樣就造成大量雷同的代碼,為了減少重復(fù)寫代碼,我們可以這樣做,重新定義一個函數(shù):專門處理日志 ,日志處理完之后再執(zhí)行真正的業(yè)務(wù)代碼
def use_logging(func):
logging.warn("%s is running" % func.__name__)
func()def bar():
print('i am bar')use_logging(bar)
邏輯上不難理解,
但是這樣的話,我們每次都要將一個函數(shù)作為參數(shù)傳遞給use_logging函數(shù)。而且這種方式已經(jīng)破壞了原有的代碼邏輯結(jié)構(gòu),之前執(zhí)行業(yè)務(wù)邏輯時,執(zhí)行運行bar(),但是現(xiàn)在不得不改成use_logging(bar)。那么有沒有更好的方式的呢?當然有,答案就是裝飾器。
簡單裝飾器
def use_logging(func):
def wrapper(*args, **kwargs):
logging.warn("%s is running" % func.__name__)
return func(*args, **kwargs)
return wrapperdef bar():
print('i am bar')bar = use_logging(bar)bar()
函數(shù)use_logging就是裝飾器,它把執(zhí)行真正業(yè)務(wù)方法的func包裹在函數(shù)里面,看起來像bar被use_logging裝飾了。在這個例子中,函數(shù)進入和退出時
,被稱為一個橫切面(Aspect),這種編程方式被稱為面向切面的編程(Aspect-Oriented Programming)。
@符號是裝飾器的語法糖,在定義函數(shù)的時候使用,避免再一次賦值操作
def use_logging(func):
def wrapper(*args, **kwargs):
logging.warn("%s is running" % func.__name__)
return func(*args)
return wrapper@use_loggingdef foo():
print("i am foo")@use_loggingdef bar():
print("i am bar")bar()
如上所示,這樣我們就可以省去bar =
use_logging(bar)這一句了,直接調(diào)用bar()即可得到想要的結(jié)果。如果我們有其他的類似函數(shù),我們可以繼續(xù)調(diào)用裝飾器來修飾函數(shù),而不用重復(fù)修改函數(shù)或者增加新的封裝。這樣,我們就提高了程序的可重復(fù)利用性,并增加了程序的可讀性。
裝飾器在Python使用如此方便都要歸因于Python的函數(shù)能像普通的對象一樣能作為參數(shù)傳遞給其他函數(shù),可以被賦值給其他變量,可以作為返回值,可以被定義在另外一個函數(shù)內(nèi)。
帶參數(shù)的裝飾器
裝飾器還有更大的靈活性,例如帶參數(shù)的裝飾器:在上面的裝飾器調(diào)用中,比如@use_logging,該裝飾器唯一的參數(shù)就是執(zhí)行業(yè)務(wù)的函數(shù)。裝飾器的語法允許我們在調(diào)用時,提供其它參數(shù),比如@decorator(a)。這樣,就為裝飾器的編寫和使用提供了更大的靈活性。
def use_logging(level):
def decorator(func):
def wrapper(*args, **kwargs):
if level == "warn":
logging.warn("%s is running" % func.__name__)
return func(*args)
return wrapper
return decorator@use_logging(level="warn")def foo(name='foo'):
print("i am %s" % name)foo()
上面的use_logging是允許帶參數(shù)的裝飾器。它實際上是對原有裝飾器的一個函數(shù)封裝,并返回一個裝飾器。我們可以將它理解為一個含有參數(shù)的閉包。當我
們使用@use_logging(level="warn")調(diào)用的時候,Python能夠發(fā)現(xiàn)這一層的封裝,并把參數(shù)傳遞到裝飾器的環(huán)境中。
類裝飾器
再來看看類裝飾器,相比函數(shù)裝飾器,類裝飾器具有靈活度大、高內(nèi)聚、封裝性等優(yōu)點。使用類裝飾器還可以依靠類內(nèi)部的\_\_call\_\_方法,當使用 @ 形式將裝飾器附加到函數(shù)上時,就會調(diào)用此方法。
class Foo(object):
def __init__(self, func):
self._func = func
def __call__(self):
print ('class decorator runing')
self._func()
print ('class decorator ending')
@Foo
def bar():
print ('bar')
bar()
functools.wraps
使用裝飾器極大地復(fù)用了代碼,但是他有一個缺點就是原函數(shù)的元信息不見了,比如函數(shù)的docstring、__name__、參數(shù)列表,先看例子:
裝飾器
def logged(func):
def with_logging(*args, **kwargs):
print func.__name__ + " was called"
return func(*args, **kwargs)
return with_logging
函數(shù)
@loggeddef f(x):
"""does some math"""
return x + x * x
該函數(shù)完成等價于:
def f(x):
"""does some math"""
return x + x * xf = logged(f)
不難發(fā)現(xiàn),函數(shù)f被with_logging取代了,當然它的docstring,__name__就是變成了with_logging函數(shù)的信息了。
print f.__name__ ? ?# prints 'with_logging'print f.__doc__ ? ? # prints None
這個問題就比較嚴重的,好在我們有functools.wraps,wraps本身也是一個裝飾器,它能把原函數(shù)的元信息拷貝到裝飾器函數(shù)中,這使得裝飾器函數(shù)也有和原函數(shù)一樣的元信息了。
from functools import wrapsdef logged(func):
@wraps(func)
def with_logging(*args, **kwargs):
print func.__name__ + " was called"
return func(*args, **kwargs)
return with_logging@loggeddef f(x):
"""does some math"""
return x + x * xprint f.__name__ ?# prints 'f'print f.__doc__ ? # prints 'does some math'
內(nèi)置裝飾器
@staticmathod、@classmethod、@property
裝飾器的順序
@a@b@cdef f ():
等效于
f = a(b(c(f)))
就是一個callable object。 它使python編程更加容易。
例如:
@dec
def A(args):
pass
它就等價于dec(A). 當然還有帶參數(shù)的decorator。我就不舉例了。
python文檔里有這樣一句話。
A function definition may be wrapped by one or more decorator expressions. Decorator expressions are evaluated when the function is defined, in the scope that contains the function definition. The result must be a callable, which is invoked with the function object as the only argument. The returned value is bound to the function name instead of the function object. Multiple decorators are applied in nested fashion.
大概就是說函數(shù)的定義可以用多個decorator。decorator就在函數(shù)定義時用函數(shù)作為參數(shù)調(diào)用,然后返回一個可調(diào)用對象。 所以寫decorator的時候一定要返回一個可調(diào)用對象。
不知道你明白沒。