簡(jiǎn)介
Python 裝飾器是一個(gè)可調(diào)用的(函數(shù)、方法或類),它獲得一個(gè)函數(shù)對(duì)象 func_in 作為輸入,并返回另一函數(shù)對(duì)象 func_out。它用于擴(kuò)展函數(shù)、方法或類的行為。
裝飾器模式通常用于擴(kuò)展對(duì)象的功能。在日常生活中,這種擴(kuò)展的例子有:在槍上加一個(gè)消音器,使用不同的相機(jī)鏡頭等等。
Django框架中有大量裝飾器
- 限制某些HTTP請(qǐng)求對(duì)視圖的訪問
- 控制
- 按單個(gè)視圖控制壓縮
- 基于特定HTTP請(qǐng)求頭控制緩存
Pyramid框架和Zope應(yīng)用服務(wù)器也使用裝飾器來(lái)實(shí)現(xiàn)各種目標(biāo)。
- 將函數(shù)注冊(cè)為事件訂閱者
- 以特定權(quán)限保護(hù)一個(gè)方法
- 實(shí)現(xiàn)適配器模式
應(yīng)用
裝飾器模式在跨領(lǐng)域方面大放異彩:
- 數(shù)據(jù)驗(yàn)證
- 緩存
- 日志
- 監(jiān)控
- 調(diào)試
- 業(yè)務(wù)規(guī)則
- 加密
使用修飾器模式的另一個(gè)常見例子是(Graphical User Interface,GUI)工具集。在GUI工具集中,我們希望能夠?qū)⒁恍┨匦?比如邊框、陰影、顏色以及滾屏,添加到組件/控件。
第一類對(duì)象
裝飾器是Python中非常強(qiáng)大和有用的工具,它允許程序員修改函數(shù)或類的行為。裝飾器允許我們封裝另一個(gè)函數(shù),以擴(kuò)展被封裝函數(shù)的行為,而不需要修改它。但在深入研究裝飾器之前,讓我們先了解一些概念,這些概念在學(xué)習(xí)裝飾器時(shí)將會(huì)很有用。
在Python中,函數(shù)是第一類對(duì)象,這意味著 Python 中的函數(shù)可以作為參數(shù)使用或傳遞。
第一類函數(shù)的屬性:
- 函數(shù)是對(duì)象類型的實(shí)例
- 可以將函數(shù)存儲(chǔ)在變量
- 可以將函數(shù)作為參數(shù)傳遞給其他函數(shù)
- 可以從函數(shù)中返回函數(shù)。
- 可以將它們存儲(chǔ)在數(shù)據(jù)結(jié)構(gòu)中,如哈希表、列表、...
例1:將函數(shù)視為對(duì)象。
def shout(text):
return text.upper()
print(shout('Hello'))
yell = shout
print(yell('Hello'))
輸出:
HELLO
HELLO
例2:將函數(shù)作為參數(shù)傳遞
def shout(text):
return text.upper()
def whisper(text):
return text.lower()
def greet(func):
# storing the function in a variable
greeting = func("""Hi, I am created by a function passed as an argument.""")
print (greeting)
greet(shout)
greet(whisper)
輸出:
HI, I AM CREATED BY A FUNCTION PASSED AS AN ARGUMENT.
hi, i am created by a function passed as an argument.
例3: 從函數(shù)中返回函數(shù)。
def shout(text):
return text.upper()
def whisper(text):
return text.lower()
def greet(func):
# storing the function in a variable
greeting = func("""Hi, I am created by a function passed as an argument.""")
print (greeting)
greet(shout)
greet(whisper)
輸出:
25
參考資料
-
本文涉及的python中文資源 請(qǐng)?jiān)趃ithub上點(diǎn)贊,謝謝!
-
本文相關(guān)書籍下載
-
https://www.geeksforgeeks.org/decorators-in-python/
-
https://realpython.com/primer-on-python-decorators/
裝飾器
如上所述,裝飾器是用來(lái)修改函數(shù)或類的行為的。在裝飾器中,函數(shù)被當(dāng)作函數(shù)的參數(shù),然后在封裝函數(shù)中調(diào)用。
- 裝飾器的語(yǔ)法:
@gfg_decorator
def hello_decorator():
print("Gfg")
'''Above code is equivalent to -
def hello_decorator():
print("Gfg")
hello_decorator = gfg_decorator(hello_decorator)'''
gfg_decorator 是一個(gè)可調(diào)用的函數(shù),它將在另一個(gè)可調(diào)用的函數(shù)hello_decorator函數(shù)上面添加一些代碼,并返回封裝函數(shù)。
- 裝飾器可以修改行為:
# defining a decorator
def hello_decorator(func):
# inner1 is a Wrapper function in
# which the argument is called
# inner function can access the outer local
# functions like in this case "func"
def inner1():
print("Hello, this is before function execution")
# calling the actual function now
# inside the wrapper function.
func()
print("This is after function execution")
return inner1
# defining a function, to be called inside wrapper
def function_to_be_used():
print("This is inside the function !!")
# passing 'function_to_be_used' inside the
# decorator to control its behaviour
function_to_be_used = hello_decorator(function_to_be_used)
# calling the function
function_to_be_used()
輸出:
Hello, this is before function execution
This is inside the function !!
This is after function execution
讓我們跳到另一個(gè)例子,在這個(gè)例子中,我們可以用裝飾器輕松地找出函數(shù)的執(zhí)行時(shí)間。
import time
import math
import functools
# decorator to calculate duration
# taken by any function.
def calculate_time(func):
# added arguments inside the inner1,
# if function takes any arguments,
# can be added like this.
@functools.wraps(func) # 支持內(nèi)省,一般可以不用,多用于文檔
def inner1(*args, **kwargs):
# storing time before function execution
begin = time.time()
func(*args, **kwargs)
# storing time after function execution
end = time.time()
print("Total time taken in : ", func.__name__, end - begin)
return inner1
# this can be added to any function present,
# in this case to calculate a factorial
@calculate_time
def factorial(num):
# sleep 2 seconds because it takes very less time
# so that you can see the actual difference
time.sleep(2)
print(math.factorial(num))
# calling the function.
factorial(10)
@functools.wraps裝飾器使用函數(shù)functools.update_wrapper()來(lái)更新特殊屬性,如__name__和__doc__,這些屬性在自省中使用。
輸出:
3628800
Total time taken in : factorial 2.0061802864074707
- 如果函數(shù)有返回或有參數(shù)傳遞給函數(shù),怎么辦?
在上面所有的例子中,函數(shù)都沒有返回任何東西,所以沒有問題,但人們可能需要返回的值。
def hello_decorator(func):
def inner1(*args, **kwargs):
print("before Execution")
# getting the returned value
returned_value = func(*args, **kwargs)
print("after Execution")
# returning the value to the original frame
return returned_value
return inner1
# adding decorator to the function
@hello_decorator
def sum_two_numbers(a, b):
print("Inside the function")
return a + b
a, b = 1, 2
# getting the value through return of the function
print("Sum =", sum_two_numbers(a, b))
輸出:
before Execution
Inside the function
after Execution
Sum = 3
內(nèi)部函數(shù)接收的參數(shù)是*args和**kwargs,這意味著可以傳遞任何長(zhǎng)度的位置參數(shù)的元組或關(guān)鍵字參數(shù)的字典。這使得它成為通用的裝飾器,可以裝飾具有任何數(shù)量參數(shù)的函數(shù)。
- 鏈?zhǔn)窖b飾器
鏈?zhǔn)窖b飾器是指用多個(gè)裝飾器來(lái)裝飾函數(shù)。
# code for testing decorator chaining
def decor1(func):
def inner():
x = func()
return x * x
return inner
def decor(func):
def inner():
x = func()
return 2 * x
return inner
@decor1
@decor
def num():
return 10
@decor
@decor1
def num2():
return 10
print(num())
print(num2())
輸出
400
200
上面的例子類似于調(diào)用函數(shù)---
decor1(decor(num))
decor(decor1(num2))
一些常用的裝飾器在 Python 中甚至是內(nèi)建的,它們是 @classmethod, @staticmethod, 和 @property。@classmethod 和 @staticmethod 裝飾器用于定義類命名空間中的方法,這些方法與該類的特定實(shí)例沒有關(guān)系。@property裝飾器是用來(lái)定制類屬性的getters和setters的。
- 類裝飾器
在 Python 3.7 中的新的 dataclasses 模塊中完成:
from decorators import debug, do_twice
@debug
@do_twice
def greet(name):
print(f"Hello {name}")
語(yǔ)法的含義與函數(shù)裝飾器相似。你可以通過寫PlayingCard = dataclass(PlayingCard)來(lái)進(jìn)行裝飾。
類裝飾器的一個(gè)常見用途是作為元類的一些使用情況的更簡(jiǎn)單的替代。
編寫一個(gè)類裝飾器與編寫一個(gè)函數(shù)裝飾器非常相似。唯一的區(qū)別是,裝飾器將接收類而不是函數(shù)作為參數(shù)。事實(shí)上,你在上面看到的所有裝飾器都可以作為類裝飾器工作。
- 帶參數(shù)與不帶參數(shù)的裝飾器
def repeat(_func=None, *, num_times=2):
def decorator_repeat(func):
@functools.wraps(func)
def wrapper_repeat(*args, **kwargs):
for _ in range(num_times):
value = func(*args, **kwargs)
return value
return wrapper_repeat
if _func is None:
return decorator_repeat
else:
return decorator_repeat(_func)
使用functools.partial也可達(dá)到類似效果。
以下是slowdown的演進(jìn)版本
import functools
import time
def slow_down(_func=None, *, rate=1):
"""Sleep given amount of seconds before calling the function"""
def decorator_slow_down(func):
@functools.wraps(func)
def wrapper_slow_down(*args, **kwargs):
time.sleep(rate)
return func(*args, **kwargs)
return wrapper_slow_down
if _func is None:
return decorator_slow_down
else:
return decorator_slow_down(_func)
- 有狀態(tài)的裝飾器
import functools
def count_calls(func):
@functools.wraps(func)
def wrapper_count_calls(*args, **kwargs):
wrapper_count_calls.num_calls += 1
print(f"Call {wrapper_count_calls.num_calls} of {func.__name__!r}")
return func(*args, **kwargs)
wrapper_count_calls.num_calls = 0
return wrapper_count_calls
@count_calls
def say_whee():
print("Whee!")
對(duì)函數(shù)的調(diào)用次數(shù)--存儲(chǔ)在包裝函數(shù)的函數(shù)屬性 .num_calls 中。下面是使用它的效果:
>>> say_whee()
Call 1 of 'say_whee'
Whee!
>>> say_whee()
Call 2 of 'say_whee'
Whee!
>>> say_whee.num_calls
2
維護(hù)狀態(tài)的典型方法是使用類裝飾器。
import functools
class CountCalls:
def __init__(self, func):
functools.update_wrapper(self, func)
self.func = func
self.num_calls = 0
def __call__(self, *args, **kwargs):
self.num_calls += 1
print(f"Call {self.num_calls} of {self.func.__name__!r}")
return self.func(*args, **kwargs)
@CountCalls
def say_whee():
print("Whee!")
- 單例模式
單例是只有一個(gè)實(shí)例的類。比如 None、True 和 False,可以使用 is 關(guān)鍵字來(lái)比較 None。
import functools
def singleton(cls):
"""Make a class a Singleton class (only one instance)"""
@functools.wraps(cls)
def wrapper_singleton(*args, **kwargs):
if not wrapper_singleton.instance:
wrapper_singleton.instance = cls(*args, **kwargs)
return wrapper_singleton.instance
wrapper_singleton.instance = None
return wrapper_singleton
@singleton
class TheOne:
pass
如你所見,這個(gè)類裝飾器與我們的函數(shù)裝飾器遵循相同的模板。唯一的區(qū)別是,我們使用 cls 而不是 func 作為參數(shù)名,以表明它是類裝飾器。
讓我們看看它是否有效:
>>> first_one = TheOne()
>>> another_one = TheOne()
>>> id(first_one)
140094218762280
>>> id(another_one)
140094218762280
>>> first_one is another_one
True
注意:在Python中,單例其實(shí)并不像其他語(yǔ)言那樣經(jīng)常使用,通常用全局變量來(lái)實(shí)現(xiàn)更好。
- 緩存返回值
裝飾器可以為緩存和備忘提供一個(gè)很好的機(jī)制。作為一個(gè)例子,讓我們看一下斐波那契數(shù)列的遞歸定義:
import functools
from decorators import count_calls
def cache(func):
"""Keep a cache of previous function calls"""
@functools.wraps(func)
def wrapper_cache(*args, **kwargs):
cache_key = args + tuple(kwargs.items())
if cache_key not in wrapper_cache.cache:
wrapper_cache.cache[cache_key] = func(*args, **kwargs)
return wrapper_cache.cache[cache_key]
wrapper_cache.cache = dict()
return wrapper_cache
@cache
@count_calls
def fibonacci(num):
if num < 2:
return num
return fibonacci(num - 1) + fibonacci(num - 2)
在標(biāo)準(zhǔn)庫(kù)中,最近使用最少的緩存(LRU)可作為 @functools.lru_cache。
這個(gè)裝飾器比你上面看到的那個(gè)有更多的功能。你應(yīng)該使用@functools.lru_cache而不是寫你自己的緩存裝飾器:
import functools
@functools.lru_cache(maxsize=4)
def fibonacci(num):
print(f"Calculating fibonacci({num})")
if num < 2:
return num
return fibonacci(num - 1) + fibonacci(num - 2)
maxsize參數(shù)指定了多少個(gè)最近的調(diào)用被緩存。默認(rèn)值是128,但你可以指定maxsize=None來(lái)緩存所有函數(shù)調(diào)用。然而,要注意的是,如果你要緩存許多大的對(duì)象,這可能會(huì)導(dǎo)致內(nèi)存問題。
描述器descriptor
任何定義了?__get__()
,?__set__()
?或?__delete__()
?方法的對(duì)象。當(dāng)類屬性為描述器時(shí),它的特殊綁定行為就會(huì)在屬性查找時(shí)被觸發(fā)。通常情況下,使用?a.b?來(lái)獲取、設(shè)置或刪除屬性時(shí)會(huì)在?a?的類字典中查找名稱為?b?的對(duì)象,但如果?b?是描述器,則會(huì)調(diào)用對(duì)應(yīng)的描述器方法。理解描述器的概念是更深層次理解 Python 的關(guān)鍵,因?yàn)檫@是許多重要特性的基礎(chǔ),包括函數(shù)、方法、屬性、類方法、靜態(tài)方法以及對(duì)超類的引用等等。
有關(guān)描述符的方法的詳情可參看?實(shí)現(xiàn)描述器。
class property(fget=None, fset=None, fdel=None, doc=None)
fget 是獲取屬性值的函數(shù)。 fset 是用于設(shè)置屬性值的函數(shù)。 fdel 是用于刪除屬性值的函數(shù)。并且 doc 為屬性對(duì)象創(chuàng)建文檔字符串。
class C():
def __init__(self):
self._x = None
def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
demo = C()
demo.x = 5
print(demo.x)
print(demo.getx())
執(zhí)行結(jié)果文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-499893.html
5
5
更快捷的方式:
class C():
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
demo = C()
demo.x = 5
print(demo.x)
@property 裝飾器會(huì)將 x() 方法轉(zhuǎn)化為同名的只讀屬性的 "getter",并將 x的文檔字符串設(shè)置為 "I'm the 'x' property."文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-499893.html
執(zhí)行結(jié)果
5
到了這里,關(guān)于python測(cè)試開發(fā)面試??碱}:裝飾器的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!