0%

Python2 中 旧式类与property引发的bug

缘起

话接上回,说是接手了一个82年的拉菲Python项目,这次又发现了一个新坑

项目中用了一个上下文类,用于存储本次请求的一些数据,在开发过程中我想把这个上下文类dump成json,详细分析里面的数据,然而发现上下文类的行为不符合预期

症状

上下文类大概是这样

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
30
31
32
class Context():
def __init__(self):
self._input_json = None
self._result_dict = dict()

@property
def input_json(self):
print 'get _input_json'
return self._input_json

@input_json.setter
def input_json(self, input_json):
print 'set _input_json %s ' % input_json
self._input_json = input_json

@property
def result_dict(self):
print 'get _result_dict'
return self._result_dict

@result_dict.setter
def result_dict(self, result_dict):
print 'set _result_dict %s ' % result_dict
self._result_dict = result_dict

# 我附加的to_dict方法
def to_dict(self):
tmp = {}
for k, v in self.__dict__.items():
if k.startswith('_'):
tmp[k] = v
return tmp

测试之后发现,结果明显不符合预期,两个属性只输出了一个

1
2
3
4
5
6
7
8
>>> from test_property import  Context
>>> c = Context()
... c.input_json = {'a': 1}
... c.result_dict['b'] = 2
...
get _result_dict
>>> c.to_dict()
{'_input_json': None, '_result_dict': {'b': 2}}

分析

看测试代码我们可以发现,在访问result_dict属性的时候,property是工作正常的(有对应的print)

但是对应设置input_json的时候, 却没有看到对应的print输出

所以可以断定,此处的property工作不正常。

仔细看代码后,我发现Context是旧式类。可以看到,A, B, C三中类的写法,其中AB都是旧式类<type 'classobj'>, C是新式类。(旧式类只在Python2中存在)。
我们这里Context的写法和B是一样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> class A:
... pass
...
... class B():
... pass
...
... class C(object):
... pass
...
>>> type(A)
<type 'classobj'>
>>> type(B)
<type 'classobj'>
>>> type(C)
<type 'type'>

然后自然就怀疑旧式类对property装饰器的支持存在问题。
一通google之后,确定旧式类是不支持property。

确切地说,是对property的支持不完整,具体来说有以下3点。

  • 支持property的getter
  • 不支持property的setter
  • 不支持property的赋值保护

参考