Python描述器实现类型检查

(一) 使用描述器对赋值过程做类型检查


下述代码的简要说明
  • 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)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值