星期五, 六月 22, 2007

python Tree root.[inexistent].branch ?

如果
root = Tree(value)
root.trunk.branch = value1
而 trunk 不存在,能否自动创建这个 Tree Node Container

是应该利用 root 的 __getattr__ 还是应该利用 __setattr__ ?因为这时候 trunk 根本就不存在,这时也就根本无从利用其 __setattr__,而对于 root,可以肯定的是在进行 root.trunk.branch = value1 的操作时,肯定是 __getattr__ 被调用!可以看下面的例子:
>>> class test:
... def __init__(self):
... self.x = 1
... def __getattr__(self, attr_name):
... try:
... return self.__dict__[attr_name]
... except KeyError:
... self.__dict__[attr_name] = 'inexistent'
... return self.__dict__[attr_name]
...
>>> t = test()
>>> t.x
1
>>> t.y
'inexistent'
>>> t.x.y = 2
Traceback (most recent call last):
File "", line 1, in ?
AttributeError: 'int' object has no attribute 'y'
>>> t.z.x = 2
Traceback (most recent call last):
File "", line 1, in ?
AttributeError: 'str' object has no attribute 'x'

>>> print t
Traceback (most recent call last):
File "", line 1, in ?
TypeError: 'str' object is not callable
这表明 __repr__ 已经受到了影响,那么原因何在呢?

先来看下面这个例子:
>>> class test:
... def __init__(self):
... self.x = 1
... def __getattr__(self, attr_name):
... print attr_name
... if attr_name == 'y':
... return 2
... else:
... raise AttributeError, attr_name
...
>>> t = test()
>>> t.x
1
>>> t.y
y
2
>>> print t.x
1
>>> print t
__str__
__repr__
<__main__.test>
首先可以看到,在前面的例子中 return self.__dict__[attr_name] 其实不是必须的,因为 python 自己会为我们做这些,并且做的更好,因为它会检查继承树。实际上,只有当一个 attribute 在其继承树中都找不到的时候,__getattr__ 才会被调用。

从 print t 的输出可以看出,self.__str__ 和 self.__repr__ 这两个方法实际上也是通过 __getattr__ 来寻找的,在前面的例子中,没有重载 __str__ 和 __repr__,而是对它们进行了赋值操作,将字符串 'inexistent' 赋值给了它们,当然会导致它们"not callable"。

那么,为了实现上面的 Tree 操作,并且不影响 print 操作,编码如下:
def __repr__(self):
return "" % hex(id(self))
# for k, v in self.__traverse__(): print '%s = %s;' % (k, v)
def __str__(self):
return self.__repr__()
def __getattr__(self, attr_name):
setattr(self, attr_name, Tree(None))
return self.__dict__[attr_name]
def __setattr__(self, attr_name, value):
if attr_name in self.__used_names:
raise TreeExc(_("Attribute name '%s' is reserved" % attr_name))
try:
# If self.attribute exists
existed = self.__dict__[attr_name]
if isinstance(value, Tree):
subtree = value
self.__dict__[attr_name] = subtree
# Replace the node directly
else:
# self.__dict__[attr_name]._Tree__node_value = value
# This will lead to raise TreeExc at #1, because the setattr operation of
# "self.__dict__[attr_name].attribute = value" has been affected by self.__setattr__()
subtree = existed
subtree.__dict__['_Tree__node_value'] = value
# Only replace the node value
except KeyError:
# if self.attribute does not exists, assign it a EMPTY node
if isinstance(value, Tree):
subtree = value
self.__dict__[attr_name] = subtree
else:
self.__dict__[attr_name] = Tree(value)
为了和内置的 print 显示同样的效果,使用了 return "" % hex(id(self)),这里 id(self) 就是得到内存地址。

但是这里还有一个疑问,在前面那个例子中,因为 __str__ 和 __repr__ 的 attr_name 已经被打印出来,并且它又不是 "y",为什么没有抛出 AttributeError 的异常?

没有评论: