12.類裝飾器是什么(用于裝飾類的裝飾器)
注意與裝飾器類的區(qū)別
- 裝飾器的原理就是將函數(shù)對象傳入,功能增強后再傳出嫂冻,因此函數(shù)可以被裝飾器裝飾;
- 同理桨仿,類也可以作為對象被傳入,也可以作為對象被傳出钱雷,所以類也可以被裝飾器裝飾伴嗡。
13.一個類實例的創(chuàng)建過程——__new__
__new__()
是一種負責(zé)創(chuàng)建類實例的靜態(tài)方法,它無需使用 @staticmethod
裝飾器修飾瘪校,且該方法會優(yōu)先 __init__()
初始化方法被調(diào)用名段。
一般情況下泣懊,覆寫 __new__()
的實現(xiàn)將會使用合適的參數(shù)調(diào)用其超類的 super().__new__()
,并在返回之前修改實例馍刮。例如:
13_newMethodInCls.py
class DemoClass:
instances_created = 0
def __new__(cls, *args, **kwargs):
print("__new__():", cls, args, kwargs)
instance = super().__new__(cls)
print('instance:', instance)
instance.number = cls.instances_created
cls.instances_created += 1
return instance
def __init__(self, attribute):
print("__init__():", self, attribute)
self.attribute = attribute
test1 = DemoClass("abc")
print('test1:', test1)
print("\n----------------\n")
test2 = DemoClass("xyz")
print('test2:', test2)
print("\n----------------\n")
print(test1.number, test1.instances_created)
print(test2.number, test2.instances_created)
輸出結(jié)果為:
__new__(): <class '__main__.DemoClass'> ('abc',) {}
instance: <__main__.DemoClass object at 0x00000161CA9F2EB0> 0
__init__(): <__main__.DemoClass object at 0x00000161CA9F2EB0> abc
test1: <__main__.DemoClass object at 0x00000161CA9F2EB0>
----------------
__new__(): <class '__main__.DemoClass'> ('xyz',) {}
instance: <__main__.DemoClass object at 0x00000161CA9F2DF0> 1
__init__(): <__main__.DemoClass object at 0x00000161CA9F2DF0> xyz
test2: <__main__.DemoClass object at 0x00000161CA9F2DF0>
----------------
0 2
1 2
Python中存在于類中的構(gòu)造方法
__init__()
負責(zé)將類實例化卡啰,而在__init__()
執(zhí)行之前,__new__()
負責(zé)制造這樣的一個實例對象匈辱,以便__init__()
去讓該實例對象更加的豐富(為其添加屬性等)。同時:
__new__()
方法還決定是否要使用該__init__()
方法亡脸,因為__new__()
可以調(diào)用其他類的構(gòu)造方法或者直接返回別的對象來作為本類的實例。
如果將類比喻為工廠大州,那么init()方法則是該工廠的生產(chǎn)工人垂谢,init()方法接受的初始化參 數(shù)則是生產(chǎn)所需原料厦画,init()方法會按照方法中的語句負責(zé)將原料加工成實例以供工廠出貨埂陆。而 new()則是生產(chǎn)部經(jīng)理,new()方法可以決定是否將原料提供給該生產(chǎn)部工人购裙,同時它還決定著出 貨產(chǎn)品是否為該生產(chǎn)部的產(chǎn)品,因為這名經(jīng)理可以借該工廠的名義向客戶出售完全不是該工廠的產(chǎn)品躏率。
需要注意的是
- 在實例化
test1
的時候民鼓,“項目經(jīng)理”把自己的cls.instances_created
和test1.instances_created
地址弄成一樣的了 - 在在實例化
test2
的時候,“項目經(jīng)理”還是那個項目經(jīng)理丰嘉,把自己的cls.instances_created
地址改變賦給test2.instances_created
,此時test1.instances_created
地址跟著改變耍贾。
所以最后test1
阅爽、test2
的instances_created
屬性值一樣荐开。
用這個原理,就可以將__new__
重新覆寫百侧,讓他返回單例
class Singleton(object):
def __new__(cls):
# 關(guān)鍵在于這,每一次實例化的時候佣渴,我們都只會返回這同一個instance對象
if not hasattr(cls, 'instance'):
cls.instance = super(Singleton, cls).__new__(cls)
return cls.instance
obj1 = Singleton()
obj2 = Singleton()
obj1.attr1 = 'value1'
print(obj1.attr1, obj2.attr1)
print(obj1 is obj2)
輸出結(jié)果為:
value1 value1
True
14.構(gòu)造類裝飾器——用函數(shù)裝飾器
假設(shè)有很多類赫粥,但是都需要加上一個輸出偶數(shù)的方法,以14_decoratorForClsExample.py
其中的一個NumProcessing
類為例
def print_evens(self):
for i in range(self.num):
if i % 2 == 0:
print(i)
def decorator(cls):
print("開始對類進行裝飾")
cls.print_evens = print_evens
return cls
@decorator
class NumProcessing:
num = 1000
def __init__(self, num):
print("開始初始化")
self.num = num
def print_odds(self):
for i in range(self.num):
if i % 2 == 1:
print(i)
if __name__ == '__main__':
num_processing = NumProcessing(5)
num_processing.print_odds()
num_processing.print_evens()
輸出結(jié)果為;
開始對類進行裝飾
開始初始化
1
3
0
2
4
這個裝飾器的邏輯就是
- 將
NumProcessing
這個類對象傳入decorator
裝飾器中 - 然后在裝飾器中新增成員
print_evens
- 再將輸出偶數(shù)的函數(shù)對象
print_odds
傳入新增成員print_evens
,使其成為方法 - 最后將裝飾好的類返回
總結(jié):
用大白話說灵迫,@裝飾器
這個語法糖就是把被裝飾的函數(shù)對象作為參數(shù)傳進@
后面跟著的東西再加個括號,比如如果是@A
挣跋,那么被裝飾的函數(shù)其實就是被包裹進A()
里面,如果@A()
避咆,那么就是被包裹進A()()
里面因為A()
返回的才是起裝飾作用的函數(shù)修噪,A()
傳進去的是裝飾器的參數(shù),這也是為什么裝飾函數(shù)或者裝飾類需要能調(diào)用的原因黄琼。