hasattr诉__dict__的会员资格测试

时间:2016-01-06 21:02:58

标签: python python-3.x

我正在定义一个可以嵌套的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]

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 的所有问题,因为它以下划线开头。