(一) 使用描述器對指派過程做類型檢查
下述代碼的簡要說明:
- a = A(1,’yhy’) 執行個體化A類的時候,self.x通路的x是類變量TypeCheck(‘a’,int),首先會初始化TypeCheck類,由于是self.x = x指派會調用set方法,在set方法裡面,instance.dict[self.srcType] = value, 就已經将指派完成了。
- a.x 取值的時候,需要self.x,同樣是調用self.TypeCheck,也會初始化,初始化的時候,在init方法裡面會拿到self.srcType和self.dstType的值,拿到了self.srcType和self.dstType值在get方法裡面,才會知道instance__dict__[key]的key是什麼,依據對應的key值取出對于的value,最後傳回。是以最後拿到的是instance__dict__[key]的值
class TypeCheck:
def __init__(self, srcType, dstType):
self.srcType = srcType
self.dstType = dstType
# instance == a , cls == A
def __get__(self, instance, cls):
if instance is None:
return self
return instance.__dict__[self.srcType]
def __set__(self, instance, value):
if isinstance(value,self.dstType):
instance.__dict__[self.srcType] = value
else:
raise TypeError('{} should be {}'.format(value,self.dstType))
class A:
# 這個a,b 作為字典的key隻要不一樣就行
x = TypeCheck('a',int)
y = TypeCheck('b',str)
def __init__(self, x, y):
self.x = x
self.y = y
a = A(,'yhy')
print(a.x,a.y)
(二) 在裝飾器中調用描述器做類型檢查
下述代碼的簡要說明:
- 通過setattr魔術方法,對Person類進行了修改,這裡的name作為類屬性,name = TypeCheck(name, required_type),這樣就将Person類進行了改造,使得Person類有了兩個類變量,一個是name = TypeCheck(‘name’, required_type), 另一個是age = TypeCheck(‘age’, required_type)
- 是以,Person(‘yhy’,18) 初始化的時候,self.name 中的name不是執行個體變量而是類變量,會調用描述器TypeCheck
- 指派的時候,就會調用set方法,取值的時候會調用get方法
# Python write by yhy
from functools import wraps
class TypeCheck:
def __init__(self, srcType, dstType):
self.srcType = srcType
self.dstType = dstType
# instance == a , cls == A
def __get__(self, instance, cls):
if instance is None:
return self
return instance.__dict__[self.srcType]
def __set__(self, instance, value):
if isinstance(value,self.dstType):
instance.__dict__[self.srcType] = value
else:
raise TypeError('{} should be {}'.format(value,self.dstType))
# 函數裝飾器裝飾的是一個Person類
# 描述器描述類與描述函數是不一樣的
def typeassert(**kwargs):
def dec(cls):
for name, required_type in kwargs.items():
setattr(cls, name, TypeCheck(name, required_type))
return cls
return dec
@typeassert(name=str, age=int)
class Person:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
p = Person('yhy',)
print(p.name)
裝飾器裝飾的Person類變為:
# 裝飾器修改後的Person類
class Person:
# 裝飾器使得Person類多了兩個類變量name和age
name = TypeCheck('name',str)
age = TypeCheck('age',int)
def __init__(self, name: str, age: int):
self.name = name
self.age = age
(三) 導入inspect庫,通過signature函數分析裝飾器中類的參數,實作對裝飾器中類的裝飾
簡要介紹inspect庫的signature函數:
# signature庫可以對擷取類初始化的參數的變量名和變量類型
from inspect import signature
class A:
def __init__(self, x: int, y: str):
pass
print(signature(A).parameters)
print(signature(A).parameters.items())
for key, value in signature(A).parameters.items():
print(key)
# 取出類型
print(value.annotation)
# print(key) 和 print(value.annotation) 的輸出結果為
x # 這是參數變量名
<class 'int'> # 這是變量名對應的類型
y
<class 'str'>
通過上述的inspect庫的signature函數的簡要介紹,那麼就可以通過signature函數擷取變量名,進而實作無需給裝飾器顯示的傳遞參數,可以直接分析需要裝飾的類,擷取類初始化需要傳遞的參數名和參數的類型,在通過參數名和參數的類型裝飾需要裝飾的類
from inspect import signature
class TypeCheck:
def __init__(self, srcType, dstType):
self.srcType = srcType
self.dstType = dstType
# instance == a , cls == A
def __get__(self, instance, cls):
if instance is None:
return self
return instance.__dict__[self.srcType]
def __set__(self, instance, value):
if isinstance(value,self.dstType):
instance.__dict__[self.srcType] = value
else:
raise TypeError('{} should be {}'.format(value,self.dstType))
def typeassert(cls):
#def dec(cls):
sig = signature(cls).parameters.items()
for key, value in sig:
setattr(cls, key, TypeCheck(key,value.annotation))
return cls
#return dec
@typeassert
class Person:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
p = Person('yhy',)
print(p.name)
print(p.age)