(1)背景
python中,對于類(自定義類)的實例對象的默認顯示既沒有太大用處,也不美觀。比如:
1 classadder:2 def __init__(self,value=0):3 self.data=value #初始化數據
4 def __add__(self,other):5 self.data+=other6>>> x=adder()7>>>print(x)
<__main__.adder. object at>8>>>x
而通過__str__或者__repr__,可以定制化(costomise)顯示,比如,下面代碼中,在子類中定義了一個返回實例字符的__repr__方法。注意,在python3中所有對象都繼承了object的__str__,也就是實例對象的默認顯示。
1 >>>classaddrepr(adder):2 def __repr__(self):3 return 'addrepr(%s)'%self.data4 >>>x=addrepr(2) #運行__init__
5 >>>x+1 #運行__add__
6 >>>x #運行__repr__
7 addrepr(3)8 >>>print(x) #運行__repr__
9 addrepr(3)10 >>>str(x),repr(x) #均運行__repr__
11 ('addrepr(3)','addrepr(3)')
View Code
當類實例化對象被打印或者轉化為字符時,如果定義了__repr__(或者__str__),那么該__repr__(或者__str__)將被自動調用,這里__repr__用了最基本的字符格式來將self.data轉化為友好的字符顯示。
(2)為什么要用兩種顯示方法
雖然__str__與__rer__的作用都是為了獲得更友好的字符顯示,但對于代碼的設計有一些細微的區別。
(a)對于print和str內建函數,程序會首先嘗試__str__函數,如果沒有__str__函數,則嘗試__repr__函數,如果沒有__repr__函數,則選用默認顯示;
(b)在其他情況下,比如交互式回應(interactive echoes),repr函數,和嵌套中,__repr__被調用,一般地,它應該為開發者返回較為詳細的顯示。
下面通過代碼說明兩種方法的不同:
1 >>>classaddstr(adder):2 def __str__(self):3 return '[value:%s]'%self.data4 >>>x=addstr(3)5 >>>x #默認顯示6 <__main__.addstr object at>
7 >>>print(x) #調用__str__8 [value:4]9 >>>str(x),repr(x)10 ('[value:4]','<__main__.addstr object at>
(c)如果同時定義了兩種方法,那么可以在不同情況下,支持不同的顯示。如下面代碼:
1 >>>classaddboth(adder):2 def __str__(self):3 return '[value:%s]'%self.data4 def __repr__(self):5 return 'addboth(%s)'%self.dat6 >>>x=addboth(4)7 >>>x+1
8 >>>x #調用__repr__
9 addboth(5)10 >>>print(x) #調用__str__
11 [value:5]12 >>>str(x),repr(x) #分別調用__str_,__repr__
13 ('[value:5]','addboth(5)')
總結下來以上幾點就是:只有在print(),str()時,才會調用__str__()(如果沒有__str__則調用__repr__),其他情況均調用__repr__,如交互式情況下單獨顯示,repr()等。
(3)使用的三點注意
(a)首先是__str__和__repr__必須均返回字符,返回其他類型,將會報錯,所以必要的話必須確保它們進行字符轉換(比如str,%s)。
(b)根據容器(container)的字符轉換,僅有當對象出現在print的頂層時,才會調用__str__;嵌套在大的對象里的對象顯示,將仍調用__repr__,下面代碼說明了這一點:
1 >>>classPrinter:2 def __init__(self,value):3 self.value=value4 def __str__(self):5 returnstr(self.value)6 >>>objs=[Printer(2),Printer(3)]7 >>>for x in objs:print(x)8
9 2
10 3
11 >>>print(objs)12 [<__main__.printer object at>]13 >>>objs14 [<__main__.printer object at>,<__main__.printer object at>]
為確保不論有無容器,在所有情況下顯示設定的顯示模式,用__repr__,不用__str__,用如下代碼進行說明:
1 >>> classPrinter:2 def __init__(self,value):3 self.val=value4 def __repr__(self): #如果沒有__str__,調用__repr__
5 return '%s'%self.val6
7
8 >>> objs=[Printer(2),Printer(3)]9 >>> for x in objs:print(x)10
11 2
12 3
13 >>> print(objs) #調用__repr__
14 [2, 3]15 >>>objs16 [2, 3]
(c)第三,也是最為微妙的,顯示方法在極少情況下有時又也有可能觸發無限迭代循環(infinite recursion loops),因為一些對象的顯示包括了其他對象的的顯示,而一個顯示觸發了正在被顯示的對象的顯示,因而進入無限循環中。如下代碼:
"""this scripts is intended to illustrate the infinite recursion loops
caused by __repr__ overloading methods. displaying the value of a method,line10 in this script, can trigger the __repr__
of the class method, then the __repr__ method is called again, and the infinite recursion loops happen."""
classBase:def __init__(self):
self.data=1
defprint0(self):pass
defprint1(self):
a=str(getattr(self,'print0')) #Caution! getattr(object,attrname),attrname shall be string.
returnaclassNormal(Base):def __str__(self):return '%s'%self.print1()classRecursion(Base):def __repr__(self):return '%s'%self.print1()if __name__=='__main__':
a=Normal()
b=Recursion()print(a)try:print(b)exceptRecursionError:print('A recusion error happens')
運行結果為:
>A recusion error happens