### Kubernetes 概述 當下,我們很多項目於都在`Cloud Native`(雲原生)的上面,這種方法旨在使組織能夠確保可用性並快速響應和適應變化,雲原生其實就是一組本質上支持在不同雲環境(公共雲、私有雲或混合雲)上大規模構建、運行和管理應用程式的實踐和技術。 雲原生離不開兩個概念:`容 ...
簡介
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上點贊,謝謝!
裝飾器
如上所述,裝飾器是用來修改函數或類的行為的。在裝飾器中,函數被當作函數的參數,然後在封裝函數中調用。
- 裝飾器的語法:
@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 = 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 = 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