[笔记] __str__和__repr__学习笔记
>今天突发奇想打算把所知道的各种魔法方法, 就是`__x__`方法记录一下, 以后可以拿来看, 也可以和大家一起讨论.\这次打算记录两个简单的`__`方法, 分别是`__str__和__repr__`
# 前言
Python中, print语句打印一个对象, 比如
``` python
class Test(object):
pass
print(Test)
print(Test())
```
```python
<class '__main__.Test'>
<__main__.Test object at 0x00000178E84F8460>
```
打印的和我们正常见到的没什么区别, 但是`却得不到任何信息`, 打印一个对象, 很多时候我们希望打印的时候可以告诉我们一些信息, 比如一个列表, 我们打印它, 输出的
```python
>>> print()
```
而不是那么<list object at 0x00000178E84F8460>, 因为我们打印列表的需求就是想看到列表中的内容, 以便我们进行调试, 看看是不是符合我们的预期等等, 如果打印一个<list object at 0x00000178E84F8460>, 那我们什么都得不到, 总不能每个地方都进行for循环打印出来吧. 显然太麻烦了. 那么如何自定义一个类的打印, 就是与`__str__`和`__repr__`有关了.
# `__str__`
我们可以在一个类中定义`__str__`方法, 这个方法返回的必须是一个字符串, 当我们调用print(xxx)时, 就会调用xxx的`__str__`方法, 打印出这个方法返回的字符串.
```python
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)
```
打印结果
```python
<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)`
```python
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>`, 更准确的应该是这样的
```python
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__()`创建一个字符串实例.
```python
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)
```
输出
```python
<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
Python 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:57:54) on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
```
就会进入交互环境, 我们不再用编辑器来进行代码编辑了, 而是一步一步的执行, 下面我们运行
```python
>>> a = 1
>>> a
1
>>>
```
注意第二行, 只有一个a, 然后下面发现1被输出了, 我们并没有执行print语句, 如果这个代码在ide或者编辑器中写完了, 保存后python xxx.py运行, 它是不会打印的, `只有在控制台中(交互环境)`, 输入一个变量, 然后回车, 才会打印它的信息.这个时候打印的信息, 就是那个类的`__repr__`返回的字符串, 而不再是`__str__`返回的字符串了.
```python
>>> 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__`方法来进行打印.
```python
class Point(object):
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __repr__(self):
return "__repr__"
print(Point())
```
输出
```python
__repr__
```
到这里就差不多完了, 下面记录一下与这个有关的东西, 与这次笔记关系不是那么大.
## 关于字符串格式化
```python
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))
```
输出
```python
__str__
__repr__
__str__
__repr__
```
s格式化的意思是返回`xxx.__str__`的字符串信息, r格式化返回的是`xxx.__repr__`. 一开始学了,后来放弃了
页:
[1]