星期四, 六月 14, 2007

python reassign __repr__ or normal method?

已知在 python 中,变量名都是到对象的引用,因此可以指向任何对象而不受类型的限制。比如一个 function/method 的变量名就可以指向一个普通变量。那么对于象 __getattr__ 这样的 builtins 的方法会有什么影响呢?例如:
sh$ expand -t4 test001.py
#!/usr/bin/env python

class tree:
def __init__(self):
self.data = 1
def func(self):
return self.data
def __custom__(self):
return self.data
def __private(self):
return self.data

t = tree()
print "t", t
print "t.data", t.data
print "t.__custom__()", t.__custom__()
print "t._tree__private__()", t._tree__private()
# print "t.__repr__()", t.__repr__() #(1)
# print "t._tree__repr__()", t._tree__repr__() #(2)
# print "type(t.__repr__)", type(t.__repr__)
print "t.func()", t.func()
t.__repr__ = 2
t.__str__ = 2
t.func = 2
t.__custom__ = 2
t._tree__private = 2
print "*** After resign ***"
# print "t", t #(3)
# t.__repr__() is used
print "t.data", t.data
# print "t.__repr__()", t.__repr__() #(4)
# print "type(t.__repr__)", type(t.__repr__)
# print "t.func()", t.func() #(5)
# print "t.__custom__()", t.__custom__() #(6)
print "t._tree__private__()", t._tree__private() #(7)
分别看一下(1)~(6)的出错输出如下:
(1)
t <__main__.tree instance at 0xb7f0418c>
t.data 1
t.__custom__() 1
t._tree__private__() 1
t.__repr__()
Traceback (most recent call last):
File "test001.py", line 18, in ?
print "t.__repr__()", t.__repr__() #(1)
AttributeError: tree instance has no attribute '__repr__'

(2)
t <__main__.tree instance at 0xb7faa18c>
t.data 1
t.__custom__() 1
t._tree__private__() 1
t._tree__repr__()
Traceback (most recent call last):
File "test001.py", line 19, in ?
print "t._tree__repr__()", t._tree__repr__() #(2)
AttributeError: tree instance has no attribute '_tree__repr__'

(3)
t <__main__.tree instance at 0xb7f6d18c>
t.data 1
t.__custom__() 1
t._tree__private__() 1
t.func() 1
*** After resign ***
t Traceback (most recent call last):
File "test001.py", line 28, in ?
print "t", t #(3)
TypeError: 'int' object is not callable

(4)
t <__main__.tree instance at 0xb7f0e18c>
t.data 1
t.__custom__() 1
t._tree__private__() 1
t.func() 1
*** After resign ***
t.data 1
t.__repr__()
Traceback (most recent call last):
File "test001.py", line 31, in ?
print "t.__repr__()", t.__repr__() #(4)
TypeError: 'int' object is not callable

(5)
t <__main__.tree instance at 0xb7fb418c>
t.data 1
t.__custom__() 1
t._tree__private__() 1
t.func() 1
*** After resign ***
t.data 1
t.func()
Traceback (most recent call last):
File "test001.py", line 33, in ?
print "t.func()", t.func() #(5)
TypeError: 'int' object is not callable

(6)
t <__main__.tree instance at 0xb7f6818c>
t.data 1
t.__custom__() 1
t._tree__private__() 1
t.func() 1
*** After resign ***
t.data 1
t.__custom__()
Traceback (most recent call last):
File "test001.py", line 34, in ?
print "t.__custom__()", t.__custom__() #(6)
TypeError: 'int' object is not callable

(7)
t <__main__.tree instance at 0xb7f8618c>
t.data 1
t.__custom__() 1
t._tree__private__() 1
t.func() 1
*** After resign ***
t.data 1
t._tree__private__()
Traceback (most recent call last):
File "test001.py", line 35, in ?
print "t._tree__private__()", t._tree__private() #(7)
TypeError: 'int' object is not callable
这样看来,内置的操作符与一般方法一样,上面重新定义了 t.__repr__ 和 t.__str__ 后,print t 的操作就不行了(注意不是 print t.data!)。对于其他内置类型如 list/dict 等也是一样。

只是不太明白为什么上面会出现 AttributeError: tree instance has no attribute '__repr__' 这样的异常,因为 __repr__ 并不是私有变量呀?而且即使是私有变量,通过 _tree__repr__ 也应该可以访问的呀?

于是再显式地定义一个 __repr__:
sh$ expand -t4 test002.py
#!/usr/bin/env python

class tree:
def __init__(self):
self.data = 1
def __repr__(self):
return "test: %d" % self.data

t = tree()
print "t", t
print "t.data", t.data
print "t.__repr__()", t.__repr__() #(1)
print "type(t.__repr__)", type(t.__repr__)
t.__repr__ = "string"
print "*** After reassign ***"
# print "t", t #(3)
# t.__repr__() is used
print "t.data", t.data
# print "t.__repr__()", t.__repr__() #(4)
print "type(t.__repr__)", type(t.__repr__)

(3)
t test: 1
t.data 1
t.__repr__() test: 1
type(t.__repr__)
*** After reassign ***
t Traceback (most recent call last):
File "test002.py", line 16, in ?
print "t", t #(3)
TypeError: 'str' object is not callable

(4)
t test: 1
t.data 1
t.__repr__() test: 1
type(t.__repr__)
*** After reassign ***
t.data 1
t.__repr__()
Traceback (most recent call last):
File "test002.py", line 19, in ?
print "t.__repr__()", t.__repr__() #(4)
TypeError: 'str' object is not callable
可以看到,重新定义之后,t.__repr__ 这个 attribute 就有了!

但为什么在前面就产生没有定义的异常,如果没有定义,那么 print t 的操作又是如何进行的呢?对于这一点,需要对 python 的基本原理有一个更清楚的认识才行。参考"python namespace"的说明。

另一个比较重要的例子:
>>> class tree:
def __init__(self): self.__repr__ = 1
>>> t = tree()
>>> print t
Traceback (most recent call last):
File "", line 1, in ?
TypeError: 'int' object is not callable
之所以讨论这些问题,是因为如果我想要定义一个 class Tree,那么就可以存在名字空间冲突的隐患!

没有评论: