總覽:
@abstractmethod:抽象方法,含abstractmethod方法的類不能實例化,繼承了含abstractmethod方法的子類必須複寫所有abstractmethod裝飾的方法,未被裝飾的可以不重寫
@ property:方法僞裝屬性,方法返回值及屬性值,被裝飾方法不能有參數,必須實例化後調用,類不能調用
@ classmethod:類方法,可以通過實例對象和類對象調用,被該函數修飾的方法第一個參數代表類本身常用cls,被修飾函數內可調用類屬性,不能調用實例屬性
@staticmethod:靜態方法,可以通過實例對象和類對象調用,被裝飾函數可無參數,被裝飾函數內部通過類名.屬性引用類屬性或類方法,不能引用實例屬性
案例講解:
@abstractmethod
用於程序接口的控制,正如上面的特性,含有@abstractmethod修飾的父類不能實例化,但是繼承的子類必須實現@abstractmethod裝飾的方法
from abc import ABC, abstractmethod
class A(ABC):
@abstractmethod
def test(self):
pass
class B(A):
def test_1(self):
print("未覆蓋父類abstractmethod")
class C(A):
def test(self):
print("覆蓋父類abstractmethod")
if __name__ == '__main__':
a = A()
b = B()
c = C()
@ property
將一個方法僞裝成屬性,被修飾的特性方法,內部可以實現處理邏輯,但對外提供統一的調用方式,實現一個實例屬性的get,set,delete三種方法的內部邏輯,具體含義看示例code。
class Data():
def __init__(self):
self.number = 123
@property
def operation(self):
return self.number
@operation.setter
def operation(self, number):
self.number = number
return self.number
@operation.deleter
def operation(self):
del self.number
return self.number
data=Data()
data.operation=90
print(data.operation)
@ classmethod,staticmethod
類方法classmethod和靜態方法staticmethod是爲類操作準備,是將類的實例化和其方法解耦,可以在不實例化的前提下調用某些類方法。兩者的區別可以這麼理解:類方法是將類本身作爲操作對象,而靜態方法是獨立於類的一個單獨函數,只是寄存在一個類名下。類方法可以用過類屬性的一些初始化操作。
class Test():
num = "aaaa"
def __init__(self):
self.number = 123
@classmethod
def a(cls, n):
cls.num = n
print(cls.num)
@classmethod
def b(cls, n):
cls.a(n)
@classmethod
def c(cls, n):
cls.number = n
@staticmethod
def d(n):
Test.b(n)
test=Test()
test.d(11)
裝飾器函數機制
談裝飾器的原理就不得不先理解Python的閉包,在函數內部再定義一個函數,並且這個函數用到了外邊函數的變量,那麼將這個函數以及用到的一些變量稱之爲閉包即內部函數對外部函數作用域裏變量的引用(非全局變量),則稱內部函數爲閉包。
裝飾器是建立在閉包的基礎上,將被裝飾函數傳入閉包函數中執行則形成了裝飾器。
閉包:
#閉包
def counter(start=0):
count=[start]
def incr():
count[0] += 1
return count[0]
return incr
c=counter(5)
print(c())
print(c())
print(c())
print(c())
#非閉包
def counter(start=0):
count=[start]
def incr():
count[0] += 1
return count[0]
return incr()
c=counter(5)
print(c)
print(c)
print(c)
print(c)
裝飾器原型:
# -*- coding:utf-8 -*-
def B(fn):
def b():
return fn()+3
return b
@B
def add():
return 3
if __name__ == '__main__':
str = fn()
print(str)
----------------
6
當被裝飾函數帶參數:
# -*- coding:utf-8 -*-
def B(fn):
def b(*args, **kwargs):
return str(fn(*args, **kwargs))+"裝飾"
return b
@B
def fn(*args, **kwargs):
num = 0
for i in args:
num +=i
for i in kwargs.values():
num += i
return num
if __name__ == '__main__':
num = fn(1, 2, 3, a=4, b=5)
print(num)
----------------
15裝飾
當裝飾函數帶額外參數,應該在裝飾函數外再包裹一層函數
# -*- coding:utf-8 -*-
def A(n):
def B(fn):
def b(*args, **kwargs):
return n+str(fn(*args, **kwargs))+"裝飾"
return b
return B
@A("包裹前綴")
def fn(*args, **kwargs):
num = 0
for i in args:
num +=i
for i in kwargs.values():
num += i
return num
if __name__ == '__main__':
num = fn(1, 2, 3, a=4, b=5)
print(num)
----------------
包裹前綴15裝飾
當多個裝飾函數裝飾一個函數時的執行順序
# -*- coding:utf-8 -*-
def A(fn):
print(1)
def run():
print(2)
fn()
print('a')
return run
def B(fn):
print(3)
def run():
print(4)
fn()
print('b')
return run
def C(fn):
print(5)
def run():
print(6)
fn()
print('c')
return run
@A
@B
@C
def test():
print(7)
if __name__ == '__main__':
test()
----------------
5
c
3
b
1
a
2
4
6
7
由此可以得出多裝飾情況下的運行情況:初始化運行從下到上,內部執行順序從內到外。因爲開始執行時會按照裝飾順序,組裝成一個函數,而這個函數從最外層的return執行到最內層,故有上述順序,就像剝洋蔥一樣。
類裝飾器
# -*- coding:utf-8 -*-
class Add:
def __init__(self, fn):
print("初始化")
self.num = 44
self.fn = fn
def __call__(self, *args, **kwargs):
print("類裝飾器開始工作")
return self.fn(self.num)
@Add
def test(n):
return 4+n
if __name__ == '__main__':
num = test()
print(num)
----------------
初始化
類裝飾器開始工作
48
類裝飾器使用地方較少,核心是通過複寫類的回調方法__call__.
裝飾器應用場景
-
引入日誌
-
函數執行時間統計
-
執行函數前預備處理
-
執行函數後清理功能
-
權限校驗等場景
-
緩存