zoukankan      html  css  js  c++  java
  • 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__方法。注意,在python3中所有对象都继承了object的__str__,也就是实例对象的默认显示。

     1 >>>class addrepr(adder):
     2     def __repr__(self):
     3         return 'addrepr(%s)'% self.data
     4 >>>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 >>>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)')

     总结下来以上几点就是:只有在print(),str()时,才会调用__str__()(如果没有__str__则调用__repr__),其他情况均调用__repr__,如交互式情况下单独显示,repr()等。

    (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....>,<__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
    ##### 愿你一寸一寸地攻城略地,一点一点地焕然一新 #####
  • 相关阅读:
    maven POM.xml 标签详解
    Spring Boot Starter 的基本封装
    谷歌浏览器屏蔽广告的插件
    关于mysql中触发器old和new
    centos7 安装zookeeper3.4.8集群
    Flink架构、原理与部署测试
    图解Spark API
    汇编器构造
    Oracle11g CentOS7安装记录
    如何创造一门编程语言?
  • 原文地址:https://www.cnblogs.com/johnyang/p/10354787.html
Copyright © 2011-2022 走看看