(一)
使用描述器对赋值过程做类型检查
下述代码的简要说明:
- 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(1,'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',18)
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',25)
print(p.name)
print(p.age)