首先搞明白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__)
f
=
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__)
f
=
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
p
=
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
p
=
Person(
'tom'
,
11
)
print
(Person.__dict__)
print
(
type
(p))
|
本文转自zuzhou 51CTO博客,原文链接:http://blog.51cto.com/yijiu/1984201