例子:物件建構函式指定型別傳入引數(描述符與裝飾器的應用)

超人不會飛0發表於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}

  此時就可根據其新增屬性,無需累贅複寫程式碼,將想傳的的變數及型別寫入即可,會存進裝飾器的字典中進行進一步統一的加工

相關文章