首先搞明白clssmethod原理,直接修改类的dict
框架如下:
class Class_Method: def __init__(self,fn): self.fn = fn def __get__(self, instance, owner): print(self,instance,owner) return self.fn class A: @Class_Method def bar(cls): print(cls.__name__) f = A.bar print(f) f()
发现报错:
Traceback (most recent call last): <__main__.Class_Method object at 0x0000000000DE8390> NoneFile "E:/python_project/learing/20171106/class_test.py", line 32, in f() TypeError: bar() missing 1 required positional argument: 'cls'
get可以获取到,return的是bar函数
考虑A在哪里可以获取到,回到get方法中,获取owner属性,直接return owner
也就是说直接返回A
def __get__(self, instance, owner): print(self,instance,owner) return self.fn(owner)
测试:
f()
Traceback (most recent call last): File "E:/python_project/learing/20171106/class_test.py", line 33, inf() TypeError: 'NoneType' object is not callable
是一个函数调用,但是没有return任何东西
1.首先return self.fn(owner) 直接指向A类,A类可以获取
2.但是函数返回值是None,因为是def bar中没有return其他,默认是None
解决:
固定返回参数
partial
return self.fn 和 class也就是A 返回为一个新的函数
class Class_Method: def __init__(self,fn): print('init,self.fn : ', fn) self.fn = fn def __get__(self, instance,cls): print(self,instance,cls) # return self.fn(owner) return partial(self.fn,cls) class A: @Class_Method def bar(cls): print('bar:',cls.__name__) f = A.bar print(f) f()
返回如下:
init,self.fn :<__main__.Class_Method object at 0x00000000008592E8> None functools.partial( , ) bar: A
因为返回的必须是函数,所以最好使用partial 返回一个新函数
再进行调用,发现模拟使用方法是差不多的,但是模仿终归模仿
对实例的数据进行校验
涉及:inspect 参数检查
使用描述器进行参数检查
当实例化的时候必须传递参数进去,必须实例化
class Typed: def __init__(self): pass def __get__(self, instance, owner): print('get :',self, instance, owner ) def __set__(self, instance, value): print('set : ',self, instance, value) class Person: name = Typed() age = Typed() def __init__(self,name:str,age:int): self.name = name self.age = age
调用返回如下:
set : <__main__.Typed object at 0x0000000000DD8470> <__main__.Person object at 0x0000000000DB6470> tom set : <__main__.Typed object at 0x0000000000DB6400> <__main__.Person object at 0x0000000000DB6470> 1
返回了两个实例的value
判断类型:
首先需要声明并传递一个类型,
class Typed: def __init__(self,type): self.type = type def __get__(self, instance, owner): print('get :',self, instance, owner ) def __set__(self, instance, value): print('set : ',self, instance, value) class Person: name = Typed(str) age = Typed(int) def __init__(self,name:str,age:int): self.name = name self.age = age p = Person('tom',1)
获取了value,接下来自己的类型也明确了,首先传递进来是被set拦截,那么需要在set中做判断
因为设置值的时候才触发set
改进:
通过inspect 进行参数检查
inspect.signature(Person) 检查参数
print(inspect.signature(Person)) (name:str, age:int)
返回的是初始化方法,参数注解
通过parameters 返回的是一个
print(inspect.signature(Person).parameters) OrderedDict([('name',), ('age', )])
返回的是一个有序字典
使用annotation将其转为class类型
params = inspect.signature(Person).parameters for name,param in params.items(): print(name,param.annotation)
返回如下:
nameage
通过类装饰器判断数据类型
新建一个类,做为装饰器判断
class TypeAssert: def __init__(self,cls): self.cls = cls def __call__(self,name,age): params = inspect.signature(self.cls).parameters for name,param in params.items(): print(name,param.annotation) class Person: name = Typed(str) age = Typed(int) def __init__(self,name:str,age:int): self.name = name self.age = age
将这两句话去掉,不在类中调用Typed的类,将其在TypeAssert中进行调用并判断
通过setattr 创建类的属性,直接写入到字典
name,age
class TypeAssert: def __init__(self,cls): self.cls = cls def __call__(self,name,age): params = inspect.signature(self.cls).parameters for name,param in params.items(): print(name,param.annotation) if param.annotation != param.empty: #判断值是否有注解,将不等于空则加入类属性 setattr(self.cls,name,Typed(param.annotation))
这样再添加属性则被__set__拦截并修改
完整如下:
class Typed: def __init__(self,type): self.type = type def __get__(self, instance, owner): print('get :',self, instance, owner ) def __set__(self, instance, value): print('set : ',self, instance, value) if not isinstance(value,self.type): raise ValueError(value) class TypeAssert: def __init__(self,cls): self.cls = cls def __call__(self,name,age): params = inspect.signature(self.cls).parameters print(params) for name,param in params.items(): print(name,param.annotation) if param.annotation != param.empty: setattr(self.cls,name,Typed(param.annotation)) @TypeAssert class Person: name = Typed(str) age = Typed(int) def __init__(self,name:str,age:int): self.name = name self.age = age p = Person('tom',11) print(Person.__dict__) print(type(p))