我正在定义一个可以嵌套的Field类实例。最终目标是从命令行与嵌入式设备上的内存映射硬件寄存器进行交互。这里的例子是print(nop)
:
nop 1000000001000000 0X8040 32832
error ........0100.... 0X4 4 CIP
bit_0 ...........0.... 0X0 0 NO
bit_1 ..........0..... 0X0 0 NO
bit_2 .........1...... 0X1 1 YES
bit_3 ........0....... 0X0 0 NO
fault 1............... 0X1 1
我的第一次尝试是实施__getitem__
和__setitem__
,但这会导致这种互动:
> nop['error']['bit_0'] = 'NO'
> nop['fault'] = 1
这将非常繁琐,因此我想到我可能会使用__getattr__
,因为它允许我按如下方式进行交互:
> nop.error.bit_0 = 'NO'
> nop.fault = 1
使用支持自动填充功能的shell会更容易。
基本上我需要通过名称识别子字段并返回或赋值。这是我对这些方法的实现:
def __setattr__(self, key, value):
try:
self.__getattr__(key).value(value)
except AttributeError:
super().__setattr__(key, value)
def __getattr__(self, key):
if '_children' in self.__dict__:
for child in self._children:
if child.name() == key:
return child
raise AttributeError
我对此非常满意,但请注意我正在_children
中测试__dict__
的成员资格。如果我使用hasattr
,我会得到无限递归。
这是一种有缺陷的方法吗?测试__dict__
成员资格的根本原因是什么。
更新
Per Martijn Pieters我将_children
的支票添加到__setattr__
,我从_children
删除了对__getattr__
的支票,然后我首先分配了_children
__init__
。在添加子项时执行的深层复制期间,结果为RecursionError
:
def add_child(self, child, position):
assert position >= 0
child = copy.deepcopy(child)
# Coerce the child to be orphaned and positioned at 0 for the following tests.
child._position = 0
child._parent = None
...
结果
Traceback (most recent call last):
File "/home/kenny/Development/CCL/OCE/field.py", line 308, in <module>
error.add_child(child_child, i)
File "/home/kenny/Development/CCL/OCE/field.py", line 140, in add_child
child = copy.deepcopy(child)
File "/home/kenny/Miniconda/envs/python3/lib/python3.5/copy.py", line 182, in deepcopy
y = _reconstruct(x, rv, 1, memo)
File "/home/kenny/Miniconda/envs/python3/lib/python3.5/copy.py", line 299, in _reconstruct
if hasattr(y, '__setstate__'):
File "/home/kenny/Development/CCL/OCE/field.py", line 18, in __getattr__
for child in self._children:
File "/home/kenny/Development/CCL/OCE/field.py", line 18, in __getattr__
for child in self._children:
.
.
.
RecursionError: maximum recursion depth exceeded while calling a Python object
[Finished in 0.1s with exit code 1]
答案 0 :(得分:1)
applicationContext.xml
使用hasattr()
来测试是否存在属性,所以是的,在getattr()
中使用hasattr()
会导致无限递归。
在getattr()
中测试_children
:
__setattr__
因为在某些时候您希望能够def __setattr__(self, key, value):
if key == '_children':
return super().__setattr__(key, value)
try:
self.__getattr__(key).value(value)
except AttributeError:
super().__setattr__(key, value)
。
如果您将self._children = []
设置为self._children
中的第一个属性,则无需再在__init__
中测试_children
;所有实例都将具有该属性(并__getattr__
确保它已设置):
__setattr__
请注意,def __getattr__(self, key):
for child in self._children:
if child.name() == key:
return child
raise AttributeError
仅针对实例中缺少的属性进行调用,而{em>所有属性调用__getattr__
。
为了避免无限递归问题,我只是忽略所有以__setattr__
开头的属性:
_
这也巧妙地避免了尝试访问def __getattr__(self, key):
# ignore private attributes
if key[:1] != '_':
for child in self._children:
if child.name() == key:
return child
raise AttributeError
的所有问题,因为它以下划线开头。