簡介
Python 裝飾器是一個可呼叫的(函式、方法或類),它獲得一個函式物件 func_in 作為輸入,并回傳另一函式物件 func_out,它用于擴展函式、方法或類的行為,
裝飾器模式通常用于擴展物件的功能,在日常生活中,這種擴展的例子有:在槍上加一個消音器,使用不同的相機鏡頭等等,
Django框架中有大量裝飾器
- 限制某些HTTP請求對視圖的訪問
- 控制
- 按單個視圖控制壓縮
- 基于特定HTTP請求頭控制快取
Pyramid框架和Zope應用服務器也使用裝飾器來實作各種目標,
- 將函式注冊為事件訂閱者
- 以特定權限保護一個方法
- 實作配接器模式
應用
裝飾器模式在跨領域方面大放異彩:
- 資料驗證
- 快取
- 日志
- 監控
- 除錯
- 業務規則
- 加密
使用修飾器模式的另一個常見例子是(Graphical User Interface,GUI)工具集,在GUI工具集中,我們希望能夠將一些特性,比如邊框、陰影、顏色以及滾屏,添加到組件/控制元件,
第一類物件
裝飾器是Python中非常強大和有用的工具,它允許程式員修改函式或類的行為,裝飾器允許我們封裝另一個函式,以擴展被封裝函式的行為,而不需要修改它,但在深入研究裝飾器之前,讓我們先了解一些概念,這些概念在學習裝飾器時將會很有用,
在Python中,函式是第一類物件,這意味著 Python 中的函式可以作為引數使用或傳遞,
第一類函式的屬性:
- 函式是物件型別的實體
- 可以將函式存盤在變數
- 可以將函式作為引數傳遞給其他函式
- 可以從函式中回傳函式,
- 可以將它們存盤在資料結構中,如哈希表、串列、...
例1:將函式視為物件,
def shout(text):
return text.upper()
print(shout('Hello'))
yell = shout
print(yell('Hello'))
輸出:
HELLO
HELLO
例2:將函式作為引數傳遞
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: 從函式中回傳函式,
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中文資源 請在github上點贊,謝謝!
-
本文相關書籍下載
-
https://www.geeksforgeeks.org/decorators-in-python/
-
https://realpython.com/primer-on-python-decorators/
裝飾器
如上所述,裝飾器是用來修改函式或類的行為的,在裝飾器中,函式被當作函式的引數,然后在封裝函式中呼叫,
- 裝飾器的語法:
@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 是一個可呼叫的函式,它將在另一個可呼叫的函式hello_decorator函式上面添加一些代碼,并回傳封裝函式,
- 裝飾器可以修改行為:
# 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
讓我們跳到另一個例子,在這個例子中,我們可以用裝飾器輕松地找出函式的執行時間,
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) # 支持內省,一般可以不用,多用于檔案
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裝飾器使用函式functools.update_wrapper()來更新特殊屬性,如__name__和__doc__,這些屬性在自省中使用,
輸出:
3628800
Total time taken in : factorial 2.0061802864074707
- 如果函式有回傳或有引數傳遞給函式,怎么辦?
在上面所有的例子中,函式都沒有回傳任何東西,所以沒有問題,但人們可能需要回傳的值,
def hello_decorator(func):
def inner1(*args, **kwargs):
print("before Execution")
# getting the returned value
returned_value = https://www.cnblogs.com/testing-/archive/2023/06/25/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
內部函式接收的引數是*args和**kwargs,這意味著可以傳遞任何長度的位置引數的元組或關鍵字引數的字典,這使得它成為通用的裝飾器,可以裝飾具有任何數量引數的函式,
- 鏈式裝飾器
鏈式裝飾器是指用多個裝飾器來裝飾函式,
# 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
上面的例子類似于呼叫函式---
decor1(decor(num))
decor(decor1(num2))
一些常用的裝飾器在 Python 中甚至是內建的,它們是 @classmethod, @staticmethod, 和 @property,@classmethod 和 @staticmethod 裝飾器用于定義類命名空間中的方法,這些方法與該類的特定實體沒有關系,@property裝飾器是用來定制類屬性的getters和setters的,
- 類裝飾器
在 Python 3.7 中的新的 dataclasses 模塊中完成:
from decorators import debug, do_twice
@debug
@do_twice
def greet(name):
print(f"Hello {name}")
語法的含義與函式裝飾器相似,你可以通過寫PlayingCard = dataclass(PlayingCard)來進行裝飾,
類裝飾器的一個常見用途是作為元類的一些使用情況的更簡單的替代,
撰寫一個類裝飾器與撰寫一個函式裝飾器非常相似,唯一的區別是,裝飾器將接收類而不是函式作為引數,事實上,你在上面看到的所有裝飾器都可以作為類裝飾器作業,
- 帶引數與不帶引數的裝飾器
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 = https://www.cnblogs.com/testing-/archive/2023/06/25/func(*args, **kwargs)
return value
return wrapper_repeat
if _func is None:
return decorator_repeat
else:
return decorator_repeat(_func)
使用functools.partial也可達到類似效果,
以下是slowdown的演進版本
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)
- 有狀態的裝飾器
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!")
對函式的呼叫次數--存盤在包裝函式的函式屬性 .num_calls 中,下面是使用它的效果:
>>> say_whee()
Call 1 of 'say_whee'
Whee!
>>> say_whee()
Call 2 of 'say_whee'
Whee!
>>> say_whee.num_calls
2
維護狀態的典型方法是使用類裝飾器,
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!")
- 單例模式
單例是只有一個實體的類,比如 None、True 和 False,可以使用 is 關鍵字來比較 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
如你所見,這個類裝飾器與我們的函式裝飾器遵循相同的模板,唯一的區別是,我們使用 cls 而不是 func 作為引數名,以表明它是類裝飾器,
讓我們看看它是否有效:
>>> first_one = TheOne()
>>> another_one = TheOne()
>>> id(first_one)
140094218762280
>>> id(another_one)
140094218762280
>>> first_one is another_one
True
注意:在Python中,單例其實并不像其他語言那樣經常使用,通常用全域變數來實作更好,
- 快取回傳值
裝飾器可以為快取和備忘提供一個很好的機制,作為一個例子,讓我們看一下斐波那契數列的遞回定義:
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)
在標準庫中,最近使用最少的快取(LRU)可作為 @functools.lru_cache,
這個裝飾器比你上面看到的那個有更多的功能,你應該使用@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引數指定了多少個最近的呼叫被快取,默認值是128,但你可以指定maxsize=None來快取所有函式呼叫,然而,要注意的是,如果你要快取許多大的物件,這可能會導致記憶體問題,
描述器descriptor
任何定義了 __get__()
, __set__()
或 __delete__()
方法的物件,當類屬性為描述器時,它的特殊系結行為就會在屬性查找時被觸發,通常情況下,使用 a.b 來獲取、設定或洗掉屬性時會在 a 的類字典中查找名稱為 b 的物件,但如果 b 是描述器,則會呼叫對應的描述器方法,理解描述器的概念是更深層次理解 Python 的關鍵,因為這是許多重要特性的基礎,包括函式、方法、屬性、類方法、靜態方法以及對超類的參考等等,
有關描述符的方法的詳情可參看 實作描述器,
class property(fget=None, fset=None, fdel=None, doc=None)
fget 是獲取屬性值的函式, fset 是用于設定屬性值的函式, fdel 是用于洗掉屬性值的函式,并且 doc 為屬性物件創建檔案字串,
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())
執行結果
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 裝飾器會將 x() 方法轉化為同名的只讀屬性的 "getter",并將 x的檔案字串設定為 "I'm the 'x' property."
執行結果
5
釘釘或微信號: pythontesting 微信公眾號:pythontesting
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/555916.html
標籤:其他
上一篇:Kubernetes 系列:了解 k8s 架構(一)
下一篇:返回列表