通过类模拟一个classmthod

首先搞明白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 32in <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 33in <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 0x00000000008592E8None <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 0x0000000000DB64701

返回了两个实例的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

上一篇:关于如何通过Quartus II和ModelSim联合仿真


下一篇:Nginx实战教程:生产环境之Nginx高可用方案(keepalived+双机热备)