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


打印实例


我们先从类和工具当中最简朴的打印输出更先讲起,打印一个实例是一个异常不起眼的应用,然则在现实的编程当中却异常重要。缘故原由也很简朴,由于我们debug的时刻往往会想看下某个类当中的内容是不是相符我们的预期。然则我们直接print输出的话,只会获得一个地址。

我们来看一个例子:


  

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


if __name__ == "__main__":
    p = point(34)
    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当中的输出的花样稍微修改一下。

,

欧博开户网址ALLbet6.com

欢迎进入欧博开户网址(Allbet Gaming):www.aLLbetgame.us,欧博网址开放会员注册、 *** 开户、电脑客户端下载、苹果安卓下载等业务。

,

  

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__。

这两个函数若是我们只实现了一个,Python在挪用的历程当中,都市执行谁人被我们实现的。然则这两者自己的应用场景是有区别的,只是Python为了我们利便做了适配。

理论上来说,对于一个及格的__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易学难精的缘故原由。