例子:物件建構函式指定型別傳入引數(描述符與裝飾器的應用)
阿新 • • 發佈:2018-12-22
在python中,傳入引數並不像c++,java那種,有嚴格的型別檢查,可以傳入不同型別的引數給一變數。如形參name,可傳字串、整形、浮點型實參;如何對其改進?
1 class Typed: 2 def __init__(self,key,type_var): 3 self.type_var=type_var 4 self.key=key 5 def __get__(self, instance, owner): 6 print('get------>',instance) 7 print('get------>', owner) 8 return instance.__dict__[self.key] 9 def __set__(self, instance, value): 10 print('set------>',instance) 11 print('set------>', value) 12 if not isinstance(value,self.type_var): 13 raise TypeError('你輸入的%s不是%s型別'%(self.key,self.type_var))14 else: 15 instance.__dict__[self.key]=value 16 class People: 17 name=Typed('name',str) 18 age=Typed('age',int) 19 salary=Typed('salary',int) 20 def __init__(self,name,age,salary): 21 self.name=name 22 self.age=age 23 self.salary=salary 24 p=People(12,18,22014)25 ----------輸出 26 raise TypeError('你輸入的%s不是%s型別'%(self.key,self.type_var)) 27 TypeError: 你輸入的name不是<class 'str'>型別
同理,當輸入的name不是str,age不是int,salary不是int型別,會報出錯誤;People中的name,age,salay均為被描述符代理屬性,當執行21,22,23行程式碼時,當然只有當正確例項化物件後才會執行,會觸發描述符中的set設定方法。如圖輸出
1 #p=People(12,18,22014) 2 p1=People('alex',18,2201) 3 -----------輸出 4 set------> <__main__.People object at 0x00000253C8E8B438> 5 set------> alex 6 set------> <__main__.People object at 0x00000253C8E8B438> 7 set------> 18 8 set------> <__main__.People object at 0x00000253C8E8B438> 9 set------> 2201
注意的是描述符類的建構函式__init__,裡面預先設定了key,typ_var屬性,用於描述符物件name,age等傳參時方便觸發其set,get有關的操作。但是貌似還是不太簡便,若想去給規定引數傳入嚴格要求限制時,須屬性=Typed(‘屬性’,屬性的型別),如要想name為str型別,須name=Typed(‘name’,str),若想設定多個,則會有許多重複累贅程式碼。接下來利用裝飾器來改進----------------》》》
1 class Typed: 2 def __init__(self,key,type_var): 3 self.type_var=type_var 4 self.key=key 5 def __get__(self, instance, owner): 6 print('get------>',instance) 7 print('get------>', owner) 8 return instance.__dict__[self.key] 9 def __set__(self, instance, value): 10 print('set------>',instance) 11 print('set------>', value) 12 if not isinstance(value,self.type_var): 13 raise TypeError('你輸入的%s不是%s型別'%(self.key,self.type_var)) 14 else: 15 instance.__dict__[self.key]=value 16 def foo(**kwargs): 17 def wrapper(obj): 18 for key,value in kwargs.items(): 19 print('----',key,value) 20 setattr(obj,key,Typed(key,value))#People.name=Typed('name',str) 21 return obj 22 return wrapper 23 @foo(name=str,age=int,salary=int)#@wrapper---->People=wrapper(People) 24 class People: 25 # name=Typed('name',str) 26 # age=Typed('age',int) 27 # salary=Typed('salary',int) 28 def __init__(self,name,age,salary): 29 self.name=name 30 self.age=age 31 self.salary=salary 32 p=People(12,18,22014) 33 print(p.__dict__)
------》》》》》》raise TypeError('你輸入的%s不是%s型別'%(self.key,self.type_var))
TypeError: 你輸入的name不是<class 'str'>型別
1 # p=People(12,18,22014) 2 # print(p.__dict__) 3 p1=People('alex',18,2201) 4 print(p1.__dict__) 5 ---------》》 6 ---- name <class 'str'> 7 ---- age <class 'int'> 8 ---- salary <class 'int'> 9 set------> <__main__.People object at 0x00000161D311B400> 10 set------> alex 11 set------> <__main__.People object at 0x00000161D311B400> 12 set------> 18 13 set------> <__main__.People object at 0x00000161D311B400> 14 set------> 2201 15 {'name': 'alex', 'age': 18, 'salary': 2201}
此時就可根據其新增屬性,無需累贅複寫程式碼,將想傳的的變數及型別寫入即可,會存進裝飾器的字典中進行進一步統一的加工