今天突发奇想打算把所知道的各种魔法方法, 就是__x__
方法记录一下, 以后可以拿来看, 也可以和大家一起讨论.\
这次打算记录两个简单的__
方法, 分别是__str__和__repr__
前言
Python中, print语句打印一个对象, 比如
class Test(object):
pass
print(Test)
print(Test())
<class '__main__.Test'>
<__main__.Test object at 0x00000178E84F8460>
打印的和我们正常见到的没什么区别, 但是却得不到任何信息
, 打印一个对象, 很多时候我们希望打印的时候可以告诉我们一些信息, 比如一个列表, 我们打印它, 输出的
>>> print([1, 2, 3, 4])
[1, 2, 3, 4]
而不是那么<list object at 0x00000178E84F8460>, 因为我们打印列表的需求就是想看到列表中的内容, 以便我们进行调试, 看看是不是符合我们的预期等等, 如果打印一个<list object at 0x00000178E84F8460>, 那我们什么都得不到, 总不能每个地方都进行for循环打印出来吧. 显然太麻烦了. 那么如何自定义一个类的打印, 就是与__str__
和__repr__
有关了.
__str__
我们可以在一个类中定义__str__
方法, 这个方法返回的必须是一个字符串, 当我们调用print(xxx)时, 就会调用xxx的__str__
方法, 打印出这个方法返回的字符串.
class Point(object):
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return "<Point (%d, %d)>" % (self.x, self.y)
p1 = Point(1, 2)
print(p1)
打印结果
<Point (1, 2)>
Point这个类就是用来表示一个点的, 有两个属性x, y代表x和y轴的坐标, 我们如果要打印一个点, 自然希望把他的坐标打印出来, 而不是什么 <Point object in 0xxxxx>, 因此就可以通过重写__str__
方法, 来返回我们希望看到的字符串, 在返回值里面我们格式化了它的x和y.
这里拓展一下, 我们如果没定义__str__
, 打印对象也会有字符串信息, 那么是不是就代表着调用了父类的__str__
呢? 如果是, 那么父类的__str__
应该是返回的什么呢? 我们打印一个没定义__str__
的时候, 返回的是这种形式<__main__.Test object at 0x000002D075A88460>
, 那么我们猜测, object的__str__应该是这样的
, 这里的self.__class__
等价type(self)
class object:
...
def __str__(self):
return "<__main__.%s object at %s>" % (self.__class__.__name__, hex(id(self)))
...
这样子我们打印任何一个对象, 如果没重写__str__
方法都将会打印出<__main__.xx object at xx>
, 知道这个就差不多了, 不过补充一下的是, 并不是每一个打印出来前面都是<__main__ xxxx>
, 更准确的应该是这样的
return "<%s.%s object at %s>" % (self.__module__, self.__class__.__name__, hex(id(self)))
了解即可, 那个__module__只是它在的模块
, 当前运行的就是__main__
.
__str__
真的只是打印的时候才会被调用么?
其实__str__
的真正含义就是怎么把一个对象看成一个字符串, 定义了这个方法, 就自定义了怎么去把它看成字符串, 我们打印一个东西, 比如print(xxx)其实是打印xxx被看成的字符串, 可以理解为
内部会做这样的转换
print(xxx)
=> print(str(xxx))
, 先获取字符串信息, 在把那个字符串信息打印出来, 因此这里的__str__
其实就是自定义的类如何用字符串解释. 因此我们调用str(xxx)其实就是调用了 xxx.__str__()
创建一个字符串实例.
class Point(object):
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return "<Point (%d, %d)>" % (self.x, self.y)
s = str(Point(1, 2))
s = s + "123"
print(s)
输出
<Point (1, 2)>123
如果以后发现某一些魔法方法, __xxx__
, 然后又发现一些内置
函数或者类xxx
, 出了开头结尾两个__
其余的名字是一样的
比如__str__方法
和str类
, __repr__
和repr
函数, __iter__方法
和iter
函数, 那么一般情况(注意是一般情况)
,
str(xxx)
返回的是xxx.__str__()
,
iter(xxx)
返回的是xxx.__iter__()
,
int(xxx)
返回的是xxx.__int__()
,
len(xxx)
返回的是xxx.__len__()
.
__repr__
如果理解了__str__
, __repr__
理解起来会简单很多. __repr__
返回的也是一个字符串, 也用来提供信息, 不过这个提供有点特别, 它是在交互环境直接回车进行提供的. 我们首先运行python的交互环境, 在cmd的输入python
Python 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:57:54) [MSC v.1924 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
就会进入交互环境, 我们不再用编辑器来进行代码编辑了, 而是一步一步的执行, 下面我们运行
>>> a = 1
>>> a
1
>>>
注意第二行, 只有一个a, 然后下面发现1被输出了, 我们并没有执行print语句, 如果这个代码在ide或者编辑器中写完了, 保存后python xxx.py运行, 它是不会打印的, 只有在控制台中(交互环境)
, 输入一个变量, 然后回车, 才会打印它的信息. 这个时候打印的信息, 就是那个类的__repr__
返回的字符串, 而不再是__str__
返回的字符串了.
>>> class Test(object):
... def __str__(self):
... return "__str__"
...
... def __repr__(self):
... return "__repr__"
...
...
>>> Test()
__repr__
>>> print(Test())
__str__
>>>
那么联系str(xxx)
可以想到, 在控制台中, 输入一个变量xxx按回车, 其实可以看成输出repr(xxx)
或者xxx.__repr__()
返回的字符串.
这里拓展一下, 如果某一个类定义了__repr__
方法, 却没有定义__str__
方法, 当调用str(xxx)时, 找不到__str__
, 会调用这个类的__repr__
方法来进行打印.
class Point(object):
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __repr__(self):
return "__repr__"
print(Point())
输出
__repr__
到这里就差不多完了, 下面记录一下与这个有关的东西, 与这次笔记关系不是那么大.
关于字符串格式化
class Point(object):
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return "__str__"
def __repr__(self):
return "__repr__"
t = Point()
print(f"{t!s} \n{t!r}")
print("%s \n%r" % (t, t))
输出
__str__
__repr__
__str__
__repr__
s格式化的意思是返回xxx.__str__
的字符串信息, r格式化返回的是xxx.__repr__
.