首先搞明白clssmethod原理,直接修改类的dict

框架如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
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__)
=  A.bar
print (f)
f()


发现报错:

1
2
3
4
5
6
Traceback (most recent call last):
<__main__.Class_Method object at 0x0000000000DE8390> None < class  '__main__.A' >
   File  "E:/python_project/learing/20171106/class_test.py" , line  32 in  < module >
<function  A .bar at 0x00000000010F0488>
     f()
TypeError: bar() missing  1  required positional argument:  'cls'

get可以获取到,return的是bar函数

考虑A在哪里可以获取到,回到get方法中,获取owner属性,直接return owner

也就是说直接返回A

1
2
3
def  __get__( self , instance, owner):
     print ( self ,instance,owner)
     return  self .fn(owner)

测试:

f()

1
2
3
4
Traceback (most recent call last):
   File  "E:/python_project/learing/20171106/class_test.py" , line  33 in  <module>
     f()
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  返回为一个新的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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__)
=  A.bar
print (f)
f()

返回如下:

1
2
3
4
init, self .fn :  <function A.bar at  0x00000000010C1488 >
<__main__.Class_Method  object  at  0x00000000008592E8 None  < class  '__main__.A' >
functools.partial(<function A.bar at  0x00000000010C1488 >, < class  '__main__.A' >)
bar: A

因为返回的必须是函数,所以最好使用partial 返回一个新函数

再进行调用,发现模拟使用方法是差不多的,但是模仿终归模仿

对实例的数据进行校验

涉及:inspect 参数检查

使用描述器进行参数检查

当实例化的时候必须传递参数进去,必须实例化

1
2
3
4
5
6
7
8
9
10
11
12
13
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

调用返回如下:

1
2
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

判断类型:

首先需要声明并传递一个类型,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
=  Person( 'tom' , 1 )

获取了value,接下来自己的类型也明确了,首先传递进来是被set拦截,那么需要在set中做判断

因为设置值的时候才触发set

改进:

通过inspect 进行参数检查

inspect.signature(Person)  检查参数

1
2
print (inspect.signature(Person))
(name: str , age: int )

返回的是初始化方法,参数注解

通过parameters 返回的是一个

1
2
print (inspect.signature(Person).parameters)
OrderedDict([( 'name' , <Parameter  "name:str" >), ( 'age' , <Parameter  "age:int" >)])

返回的是一个有序字典

使用annotation将其转为class类型

1
2
3
params  =  inspect.signature(Person).parameters
for  name,param  in  params.items():
     print (name,param.annotation)

返回如下:

1
2
name < class  'str' >
age < class  'int' >

通过类装饰器判断数据类型

新建一个类,做为装饰器判断

1
2
3
4
5
6
7
8
9
10
11
12
13
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

1
2
3
4
5
6
7
8
9
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__拦截并修改

完整如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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
=  Person( 'tom' , 11 )
print (Person.__dict__)
print ( type (p))

本文转自zuzhou 51CTO博客,原文链接:http://blog.51cto.com/yijiu/1984201