Python 中的 __str__ 与 __repr__ 到底有什么差别

 

很多时候我们自己编写一个类,在将它的实例在终端上打印或查看的时候,我们往往会看到一个不太满意的结果。

类默认转化的字符串基本没有我们想要的一些东西,仅仅包含了类的名称以及实例的 ID (理解为 Python 对象的内存地址即可)。虽说这总比没有好,但确实是没什么用处啊。

所以,我们可能会手动打印对象的一些属性或者是在类里自己实现一个方法来返回我们需要的信息。

这没有什么不对的地方,但是我们可以使用更 Pythonic 的方式来解决这个问题。

使用 __str__ 实现类到字符串的转化

不用自己另外定义一个方法,和 JAVA 的 toString() 方法类似,你可以在类里实现__str__ 和 __repr__ 方法从而自定义类的字符串描述,这两种都是比较 Pythonic 的方式去控制对象转化为字符串的方式。

下面我们通过做实验慢慢的来看这两种方式是怎么工作的。首先,我们先加一个 __str__ 方法到前面的类中看看情况。

当你重新打印和查看这个类的实例的时候,你会看到一个稍微不同的结果

查看 my_car 的时候的输出仍然和之前一样,不过打印 my_car 的时候返回的内容和新加的 __str__ 方法的返回一致。类的 __str__ 方法会在某些需要将对象转为字符串的时候被调用。比如下面这些情况

有了 __str__ 这个方法,你就不用手动去打印对象的一些信息或者添加额外的方法去达到目的。类到字符串的转化使用 __str__ 这种 Pythonic 的方式实现即可。

使用 __repr__ 也有类似的效果

有的朋友可能发现,上面我们查看 my_car 对象的时候,输出的仍是类似 <__main__.Car object at 0x10b142128> 这样比较奇怪的结果。这是因为 Python 3 中一共有 2 中方式控制类到字符串的转化,第一种就是我们前面提到的 __str__ 方法,另一个就是 __repr__ 方法。后者的工作方式与前者类似,但是它被调用的时机不同。

Python 2 中还有一个 __unicode__ 方法,后面我会说明,暂时跳过。

这里有个简单的例子,同样是在之前的类上作改动

我们通过下面的操作来感觉下什么时候调用 __str__ ,什么时候调用的 __repr__ 。

从上面可以看出,当我们查看对象的时候(上图的最后一个操作)调用的是 __repr__ 方法。

另外,列表以及字典等容器总是会使用 __repr__ 方法。即使你显式的调用 str 方法,也是如此。

如果我们需要显示的指定以何种方式进行类到字符串的转化,我们可以使用内置的 str() 或 repr() 方法,它们会调用类中对应的双下划线方法。(当然,上面的情况除外)

当然,如果你直接调用 __str__ 或 __repr__ 方法,也能达到同样的方法,但是不推荐这么做。

那么 __str__ 和 __repr__ 的差别是什么

现在你可能在想,__str__ 和 __repr__ 的差别究竟在哪里,它们的功能都是实现类到字符串的转化,它们的特定并没有体现出用途上的差异。

带着这个这个问题,我们试着去 Python 的标准库中找找答案。我们就来看看 datetime.date 这个类是怎么在使用这两个方法的。

因此,我们有个初步的答案。

__str__ 的返回结果可读性强。也就是说,__str__ 的意义是得到便于人们阅读的信息,就像上面的 '2018-04-03' 一样。

__repr__ 的返回结果应更准确。怎么说,__repr__ 存在的目的在于调试,便于开发者使用。细心的读者会发现将 __repr__ 返回的方式直接复制到命令行上,是可以直接执行的。

上面应该就是这两个方法的意义所在吧(便于描述,后面我称这为通常的原则吧)。

但是于个人来说,如果按照通常的原则去编写代码会做很多额外的工作,两个方法的返回结果只需要对开发者友好就可以了,并不一定需要存储某个对象的完整状态。后面我会根据这一点,写部分有实践意义的代码实例,并不完全按照通常的原则。

为什么每个类都最好有一个 __repr__ 方法

如果你没有添加 __str__ 方法,Python 在需要该方法但找不到的时候,它会去调用 __repr__ 方法。因此,我推荐在写自己的类的时候至少添加一个 __repr__ 方法,这能保证类到字符串始终有一个有效的自定义转换方式。

我们为 Car 类添加一个 __repr__ 方法

注意,我们这里用了 !r 标记,是为了保证 self.color 与 self.mileage 在转化为字符串的时候使用 repr(self.color) 和 repr(self.mileage) ,而不是 str(self.color) 和 str(self.mileage) 。

这个能正常工作,不过有个缺点,就是我们把类的名称写死了。这有一个小技巧可以改进这种方式,就是使用对象的 __class__.__name__ 属性,该属性总代表着类的名称。

这样做的话,当类名被修改的时候,我们不需要修改 __repr__ 方法,这也符合软件开发的 DRY 原则( Don’t Repeat Yourself )。

这种写法也有一个不好的地方,就是格式化字符串太长了。当然,我们好好调整一个格式也能符合 PEP 8 的代码规范。

实现了 __repr__ 方法后,当我们查看类的实例或者直接调用 repr() 方法,就能得到一个比较满意的结果了。

打印或直接调用 str() 方法也能得到相同的结果,因为 __str__ 的默认实现就是调用 __repr__ 方法。

这样就能以比较少的工作量,让两个方法都能工作,并且也有一定的可读性,所以一般情况下,我都推荐至少添加一个 __repr__ 方法。

下面是比较全的代码示例

Python 2 中的 __unicode__ 方法

Python 3 中字符串用 str 类型表示,代表 unicode 字符串。而 Python 2 中字符串有两种类型,一是 str ,只能存储 ASCII 码,另一种是 unicode ,与 Python 3 中的 str 等同。

通常来说,用 __unicode__ 来控制类到字符串的转化更容易被大家接受。和 __str__ 和 __repr__ 类似,你可以使用内置的 unicode() 来显示调用 __unicode__ 方法。

打印语句和 str() 会调用 __str__ 方法,unicode() 会先找 __unicode__ 方法,找不到的话会调用 __str__ 方法,并将其结果按当时的编码方式解码返回。

相对于Python 3 ,Python 2 中的类到字符串的转化,显得稍微复杂一些。不过,下面我给了个便于实践的思路。由于使用 unicode 处理字符串更方便,这也是趋势,所以我们总会实现自己的 __unicode__ 方法。同时,__str__ 方法的实现则依靠于 __unicode__ ,主要逻辑是调用 __unicode__ 方法并将其结果使用 UTF-8 编码后返回。

所以,大部分情况下,__str__ 方法都不需要做修改,对于新建的类,可以直接把这个 __str__ 方法复制进去,而把关注点只放在 __unicode__ 方法的实现上。

下面是在 Python 2 中一段比较完整的示例

小结

* 我们可以使用 __str__ 和 __repr__ 方法定义类到字符串的转化方式,而不需要手动打印某些属性或是添加额外的方法。

* 一般来说,__str__ 的返回结果在于强可读性,而 __repr__ 的返回结果在于准确性。

* 我们至少需要添加一个 __repr__ 方法来保证类到字符串的自定义转化的有效性,__str__ 是可选的。因为默认情况下,在需要却找不到 __str__ 方法的时候,会自动调用 __repr__ 方法。

* 在 Python 2 中,我们可能更在意类的 __unicode__ 方法的实现。

__str__和__repr__的更多相关文章

  1. python 的特殊方法 __str__和__repr__

    __str__和__repr__ 如果要把一个类的实例变成 str,就需要实现特殊方法__str__(): class Person(object): def __init__(self, name, ...

  2. __str__与__repr__

    在讲解之前,我们先来了解下str和repr的区别:两者都是用来将数字,列表等类型的数据转化为字符串的形式.不同之处在于str更加类似于C语言中使用printf输出的内容,而repr输出的内容会直接将变 ...

  3. python中__str__与__repr__的区别

    __str__和repr __str__和__repr__都是python的内置方法,都用与将对象的属性转化成人类容易识别的信息,他们有什么区别呢 来看一段代码 from math import hy ...

  4. What is the difference between __str__ and __repr__ in Python

    from https://www.pythoncentral.io/what-is-the-difference-between-__str__-and-__repr__-in-python/ 目的 ...

  5. python之反射和内置函数__str__、__repr__

    一.反射 反射类中的变量 反射对象中的变量 反射模块中的变量 反射本文件中的变量 .定义:使用字符串数据类型的变量名 来获取这个变量的值 例如: name = 'xiaoming' print(nam ...

  6. python __str__() 和 __repr__()是干啥的

    1. 没定义__str__() print的时候得不到自己想要的东西 类默认转化的字符串基本没有我们想要的一些东西,仅仅包含了类的名称以及实例的 ID (理解为 Python 对象的内存地址即可).虽 ...

  7. isinstance,issubclass,内置函数__str__和__repr__,__format__,dir()函数

    isinstance(obj,cls) 检查是否obj是否是类 cls 的对象 #对象与类之间的关系 判断第一个参数是否是第二个参数的实例 # 身份运算 # 2 == 3 # 值是否相等# 2 is ...

  8. 9.4、__del__、__doc__、__dict__、__module__、__getitem__、__setitem__、__delitem__、__str__、__repr__、__call__

    相关内容: __del__.__doc__.__dict__.__module__.__getitem__.__setitem__.__delitem__.__str__.__repr__.__cal ...

  9. python 全栈开发,Day24(复习,__str__和__repr__,__format__,__call__,__eq__,__del__,__new__,item系列)

    反射: 使用字符串数据类型的变量名来使用变量 wwwh即what,where,why,how  这4点是一种学习方法 反射 :使用字符串数据类型的变量名来使用变量 1.文件中存储的都是字符串 2.网络 ...

  10. python全栈开发day23-面向对象高级:反射(getattr、hasattr、setattr、delattr)、__call__、__len__、__str__、__repr__、__hash__、__eq__、isinstance、issubclass

    一.今日内容总结 1.反射 使用字符串数据类型的变量名来操作一个变量的值. #使用反射获取某个命名空间中的值, #需要 #有一个变量指向这个命名空间 #字符串数据类型的名字 #再使用getattr获取 ...

随机推荐

  1. PAT 1088. Rational Arithmetic

    For two rational numbers, your task is to implement the basic arithmetics, that is, to calculate the ...

  2. 2.2 SVN的简单使用

    1.打开SVN服务器 选中Repositories→右键→Create new Repositories 选中Test2→右键→Copy URL to Clipboard 打开记事本粘贴地址:http ...

  3. Java代码模拟http请求的两种方式

    z这里用百度地图的逆地理编码接口为例, 第一种方式:(通过jdk中的java.net包) 引入工具类 import java.net.URL; import java.net.URLConnectio ...

  4. cannot find -lGL

    解决方法: 以下操作都在root权限下进行! 1.按照提示安装对应的库文件,fedora安装库件的格式:yum install libxxx(你要装的库),如果已经安装GL库,会显示已经安装 Ps:如 ...

  5. oracle 单独开始一个事物的写法 。

    SET TRANSACTION NAME 'Update salaries'; SAVEPOINT before_salary_update; UPDATE employees SET salary= ...

  6. 洛谷 P2010 回文日期

    Noip2016普及组T2 题目描述 在日常生活中,通过年.月.日这三个要素可以表示出一个唯一确定的日期. 牛牛习惯用8位数字表示一个日期,其中,前4位代表年份,接下来2位代表月 份,最后2位代表日期 ...

  7. [bzoj2529][Poi2011]Sticks_贪心

    Sticks bzoj-2529 Poi-2011 题目大意:给你n根木棒,每种木棒有长度和颜色,颜色共有k种,求满足条件的3根木棒使得这3根木棒颜色互不相同且可以围成三角形. 注释:$1\le n ...

  8. HDU 1788 Chinese remainder theorem again 中国剩余定理

    题意: 给定n,AA 以下n个数m1,m2···mn 则有n条方程 res % m1 = m1-AA res % m2 = m2-AA 问res的最小值 直接上剩余定理,嘿嘿 #include< ...

  9. SAP WEBSERVICE Soap中RPC-style和Document-style

    RPC是以方法调用的方式描写叙述WebSerivce的,也就是说,你要说清楚调用的那个方法,以及各个參数的名称和值.要描写叙述这些东东.SOAP消息就要有一个统一的规范,指出那一部分是方法名.哪个部分 ...

  10. 点击TButton后的执行OnClick和OnMouseDown两个事件的过程(其实是通过WM_COMMAND执行程序员的代码)

    问题的来源:在李维的<深入浅出VCL>一书中提到了点击TButton会触发WM_COMMAND消息,正是它真正执行了程序员的代码.也许是我比较笨,没有理解他说的含义.但是后来经过追踪代码和 ...