python中__str__與__repr__
(1)背景
python中,對於類(自定義類)的實例對象的默認顯示既沒有太大用處,也不美觀。比如:
1 class adder: 2 def __init__(self,value=0): 3 self.data=value #初始化數據 4 def __add__(self,other): 5 self.data+=other 6>>> x=adder()
7>>>print(x)
<__main__.adder. object at 0x.....> 8>>>x
<__main__.adder object at 0x......>
而通過__str__或者__repr__,可以定制化(costomise)顯示,比如,下面代碼中,在子類中定義了一個返回實例字符的__repr__方法。
1 >>>class addrepr(adder): 2 def __repr__(self): 3 return ‘addrepr(%s)‘% self.data 4 >>>x=addrepr(2) #運行__init__ 5 >>>x+1 #View Code運行__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)‘)
當類實例化對象被打印或者轉化為字符時,如果定義了__repr__(或者__str__),那麽該__repr__(或者__str__)將被自動調用,這裏__repr__用了最基本的字符格式來將self.data轉化為友好的字符顯示。
(2)為什麽要用兩種顯示方法
雖然__str__與__rer__的作用都是為了獲得更友好的字符顯示,但對於代碼的設計有一些細微的區別。
(a)對於print和str內建函數,程序會首先嘗試__str__函數,如果沒有__str__函數,則嘗試__repr__函數,如果沒有__repr__函數,則選用默認顯示;
(b)在其他情況下,比如交互式回應(interactive echoes),repr函數,和嵌套中,__repr__被調用,一般地,它應該為開發者返回較為詳細的顯示。
下面通過代碼說明兩種方法的不同:
1 >>>class addstr(adder): 2 def __str__(self): 3 return ‘[value:%s]‘% self.data 4 >>>x=addstr(3) 5 >>>x #默認顯示 6 <__main__.addstr object at 0x....> 7 >>>print(x) #調用__str__ 8 [value:4] 9 >>>str(x),repr(x) 10 (‘[value:4]‘,‘<__main__.addstr object at 0x...>
(c)如果同時定義了兩種方法,那麽可以在不同情況下,支持不同的顯示。如下面代碼:
1 >>>class addboth(adder): 2 def __str__(self): 3 return ‘[value:%s]‘%self.data 4 def __repr__(self): 5 return ‘addboth(%s)‘% self.dat 6 >>>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)‘)
(3)使用的三點註意
(a)首先是__str__和__repr__必須均返回字符,返回其他類型,將會報錯,所以必要的話必須確保它們進行字符轉換(比如str,%s)。
(b)根據容器(container)的字符轉換,僅有當對象出現在print的頂層時,才會調用__str__;嵌套在大的對象裏的對象顯示,將仍調用__repr__,下面代碼說明了這一點:
1 >>>class Printer: 2 def __init__(self,value): 3 self.value=value 4 def __str__(self): 5 return str(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 0x....>] 13 >>>objs 14 [<__main__.Printer object at 0x....>]
為確保不論有無容器,在所有情況下顯示定制顯示,用__repr__,不用__str__,用如下代碼進行說明:
1 >>> class Printer: 2 def __init__(self,value): 3 self.val=value 4 def __repr__(self): #如果沒有__str__,調用__repr__ 5 return ‘%s‘% self.val 6 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 >>> objs 16 [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. """ class Base: def __init__(self): self.data=1 def print0(self): pass def print1(self): a=str(getattr(self,‘print0‘)) #Caution! getattr(object,attrname),attrname shall be string. return a class Normal(Base): def __str__(self): return ‘%s‘% self.print1() class Recursion(Base): def __repr__(self): return ‘%s‘% self.print1() if __name__==‘__main__‘: a=Normal() b=Recursion() print(a) try: print(b) except RecursionError: print(‘A recusion error happens‘)
運行結果為:
<bound method Base.print0 of <__main__.Normal object at 0x02E68450>> A recusion error happens
python中__str__與__repr__