本文始发于个人公众号:TechFlow,原创不易,求个关注

今天是Python专题的第10篇文章,我们来聊聊Python当中的类。

打印实例

我们先从类和对象当中最简单的打印输出开始讲起,打印一个实例是一个非常不起眼的应用,但是在实际的编程当中却非常重要。原因也很简单,因为我们debug的时候往往会想看下某个类当中的内容是不是符合我们的预期。但是我们直接print输出的话,只会得到一个地址。

我们来看一个例子:

class point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

if __name__ == "__main__":
    p = point(3, 4)
    print(p)

在这段代码当中我们定义了一个简单的类,它当中有x和y两个元素,但是如果我们直接运行的话,屏幕上会输出这样一个结果:

<__main__.point object at 0x10a18c210>

这个是解释器在执行的时候这个实例的一些相关信息,但是对于我们来说几乎没有参考意义,我们想要的是这个实例当中具体的值,而不是一个内存当中的地址。

想要实现这个功能,我们有很多方法,下面我们一一来看。

__str__方法

__str__方法大家应该都不陌生,它类似于Java当中的toString方法,可以根据我们的需要返回实例转化成字符串之后的结果。

比如,我们可以在类当中重载这个方法,就可以根据我们的需要输出结果了:

class point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return 'x: %s, y: %s' % (self.x, self.y)

当我们运行它,得到的结果会是:

x: 3, y: 4

__str__和__init__, __len__很多函数一样是Python中的特殊函数,在我们创建类的时候,系统会我们隐式创造许多这样的特殊函数。我们可以根据需要重载其中的一部分完成我们想要的功能。比如如果我们写的是一棵二叉树的类,我们还可以在__str__函数当中进行递归遍历所有的节点,打印出完整的树来。

__repr__方法

你也许可能也听说过__repr__函数,它也可以实现根据我们的需要自定义输出的功能。比如我们把上面的代码改下函数名,也可以得到一样的结果。

class point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return 'x: %s, y: %s' % (self.x, self.y)

我们运行它,同样会得到:

x: 3, y: 4

这是为什么呢,难道__repr__和__str__是一样的吗?如果是一样的,Python的设计者干嘛要保留两个完全相同的函数呢,为什么不去掉其中一个呢?

在分析原因之前,我们先来做一个实验,如果我们两个函数都重载,那么当我们输出的时候,程序执行的是哪一个呢?为了做好区分,我们把repr当中的输出的格式稍微修改一下。

class point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return 'x: %s, y: %s' % (self.x, self.y)

    def __repr__(self):
        return '<point x: %s, y: %s>' % (self.x, self.y)

我们运行之后,会发现输出的结果还是:

x: 3, y: 4

先别着急下结论,我们再把这段代码拷贝到jupyter notebook当中,我们这次不通过打印输出,而通过jupyter自带的交互框输出交互结果,我们再来看下:

奇怪,怎么结果就变成了__repr__的结果了呢?

其实这正是反应了两者的区别,如果简单理解,这两个函数都是将一个实例转成字符串。但是不同的是,两者的使用场景不同,其中__str__更加侧重展示。所以当我们print输出给用户或者使用str函数进行类型转化的时候,Python都会默认优先调用__str__函数。而__repr__更侧重于这个实例的报告,除了实例当中的内容之外,我们往往还会附上它的类相关的信息,因为这些内容是给开发者看的。所以当我们在交互式窗口输出的时候,它会优先调用__repr__。

理论上来说,对于一个合格的__repr__函数要能够做到:

eval(repr(obj)) == obj

也就是说我们通过__repr__输出的内容执行之后可以再还原得到这个实例本身,当然在一些场景下这个非常难以实现,所以我们退而求其次,保证__repr__当中输出类和对象足够多的信息,方便开发者调试和使用即可。

另外多说一句,repr是report的缩写,所以它有一个报告的意思在里面,而str就只是转化成字符串而已。这两者还是有一定区别的。

format

Python当中最常用的输出函数除了上面两个之外,还有一个就是format

比较简单的用法就是通过{}代表变量,然后按照顺序依次输入:

除此之外,我们还可以进一步写明花括号里的变量名称,进一步增加可读性:

format的功能远不止如此,它还支持许多参数,类似于C语言当中的printf,可以通过不同的参数做到各种各样的输出。比如控制小数点后面保留的位数,或者是转化成百分数、科学记数法、左右对齐等功能。这里不一一列举了,大家用到的时候再查询即可。

我们当然可以使用format重新__repr__和__str__当中的逻辑,但这并不能体现它的强大。因为在Python当中,也为类提供了__format__这个特殊函数,通过重写__format__和使用format,我们可以做到更牛的功能。

format联合__format__

我们可以在类当中重载__format__函数,这样我们就可以在外部直接通过format函数来调用对象,输出我们想要的结果。

我们来看代码:

class point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return 'x: %s, y: %s' % (self.x, self.y)

    def __format__(self, code):
        return 'x: {x}, y: {y}'.format(x = self.x, y = self.y)

我们把刚才的__repr__改成了__format__,但是需要注意一个细节,我们多加了一个参数code,这是由于format当中支持通过参数来对处理逻辑进行配置的功能,所以我们必须要在接口处多加一个参数。加好了以后,我们就可以直接调用format(p)了。

到这里还没有结束,在有些场景当中,对于同一个对象我们可能有多种输出的格式。比如点,在有些场景下我们可能希望输出(x, y),有时候我们又希望输出x: 3, y: 4,可能还有些场景当中,我们希望输出<x, y>。

我们针对这么多场景,如果各自实现不同的接口会非常麻烦。这个时候利用__format__当中的这个参数,就可以大大简化这个过程,我们来看代码:

formats = {
    'normal': 'x: {p.x}, y: {p.y}',
    'point' : '({p.x}, {p.y})',
    'prot': '<{p.x}, {p.y}>'
}

class point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return 'x: %s, y: %s' % (self.x, self.y)

    def __format__(self, code):
        return formats[code].format(p=self)

我们在调用的时候就可以通过参数来控制我们究竟使用哪一种格式来格式化对象了:

也就是说通过重载__format__方法,我们把原本固定的格式化的逻辑做成了可配置的。这样大大增加了我们使用过程当中的灵活性,这种灵活性在一些问题场景当中可以大大简化和简洁我们的代码。对于Python这门语言来说,我个人感觉实现功能只是其中很小的一个部分,把代码写得简洁美观,才是其中的大头。这也是为什么很多人都说Python易学难精的原因。

今天的文章就是这些,如果觉得有所收获,请顺手点个关注或者转发吧,你们的举手之劳对我来说很重要。

Python——详解__str__, __repr__和__format__的更多相关文章

  1. [转载]python 详解re模块

    原文地址:python 详解re模块作者:Rocky 正则表达式的元字符有. ^ $ * ? { [ ] | ( ) .表示任意字符 []用来匹配一个指定的字符类别,所谓的字符类别就是你想匹配的一个字 ...

  2. 33 Python 详解命令解析 - argparse--更加详细--转载

    https://blog.csdn.net/lis_12/article/details/54618868 Python 详解命令行解析 - argparse Python 详解命令行解析 - arg ...

  3. python基础---- __getattribute__----__str__,__repr__,__format__----__doc__----__module__和__class__

    目录: 一. __getattribute__ 二.__str__,__repr__,__format__ 三.__doc__ 四.__module__和__class__ 一. __getattri ...

  4. 复习python的__call__ __str__ __repr__ __getattr__函数 整理

    class Www: def __init__(self,name): self.name=name def __str__(self): return '名称 %s'%self.name #__re ...

  5. python 详解re模块

    正则表达式的元字符有. ^ $ * ? { [ ] | ( ).表示任意字符[]用来匹配一个指定的字符类别,所谓的字符类别就是你想匹配的一个字符集,对于字符集中的字符可以理解成或的关系.^ 如果放在字 ...

  6. 【经典案例】Python详解设计模式:策略模式

    完成一项任务往往有多种方式,我们将其称之为策略. 比如,超市做活动,如果你的购物积分满1000,就可以按兑换现金抵用券10元,如果购买同一商品满10件,就可以打9折,如果如果购买的金额超过500,就可 ...

  7. python 详解正则表达式的使用(re模块)

    一,什么是正则表达式 正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串.将匹配的子串替换或者从某个串中取出符合某个条件 ...

  8. python详解json模块

    我们在做工作中经常会使用到json模块,今天就简单介绍下json模块 什么是json JSON ,全称为JavaScript Object Notation, 也就是JavaScript 对象标记,它 ...

  9. Python——详解collections工具库

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天为大家介绍Python当中一个很好用也是很基础的工具库,叫做collections. collection在英文当中有容器的意思,所以顾 ...

随机推荐

  1. Element UI中的上传文件功能

    上传文件给后台: <el-upload style="display:inline-block" :limit=" class="upload-demo& ...

  2. 数据库事务练习-Java(新手)

    数据库事务:       一个数据库事务通常包含了一个序列的对数据库的读/写操作.  为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法. p ...

  3. iOS包重签名工具,ipa文件重签名,快速签名,SignTool签名工具,好用的签名工具,App重签名

    新工具 ProjectTool 已上线 这是一款快速写白包工具,秒级别写H5游戏壳包,可视化操作,极易使用,支持Swift.Objecive-C双语言 QQ交流群:811715780 进入 Proje ...

  4. [UWP]抄抄《CSS 故障艺术》的动画

    1. 前言 什么是故障艺术(Glitch Art 风)?我们熟知的抖音的 LOGO 正是故障艺术其中一种表现形式.它有一种魔幻的感觉,看起来具有闪烁.震动的效果,很吸引人眼球.故障艺术它模拟了画面信号 ...

  5. 一个简单的方法去掉angular application中URLs的hashtag

    本文转载自:Pretty URLs in AngularJS: Removing the # By default, AngularJS will route URLs with a hashtag. ...

  6. C语言学生管理系统

    想练习一下链表,所以就有了这个用C写的学生管理系统 没有把它写入文件,才不是因为我懒哈哈哈,主要是为了练习链表的 #include<stdio.h> #include<stdlib. ...

  7. Hook集合----SSDTHook(x86 Win7)

    最近在学习Ring0层Hook的一些知识点,很久就写完SSDTHook的代码了,但是一直没有整理成笔记,最近有时间也就整理整理. 介绍: SSDTHook 实质是利用Ntoskrnl.exe 中全局导 ...

  8. 北邮OJ 89. 统计时间间隔 java版

    89. 统计时间间隔 时间限制 1000 ms 内存限制 65536 KB 题目描述 给出两个时间(24小时制),求第一个时间至少要经过多久才能到达第二个时间.给出的时间一定满足的形式,其中x和y分别 ...

  9. 发布内容需要的Markdown语法

    发布内容需要的Markdown语法 目录 发布内容需要的Markdown语法 [toc] 1.概述 1.1设计理念 1.2内联HTML语法 1.3特殊字符自动转义 2.行内语法讲解 2.1注释的表述 ...

  10. Reactor模式和Proactor模式

    Reactor 主线程往epoll内核事件表中注册socket上的读就绪事件 主线程调用epoll_wait等待socket上有数据可读 当socket上有数据可读时,epoll_wait通知主线程, ...