Python魔法方法漫游指南:类的表示

使用字符串等信息来表示类是一个相当实用的特性。比方说你在调试代码时,会频繁使用 print() 等函数来获取对象信息,其背后就是隐式调用了将类转化为字符串的魔法方法。相对应的,还有另一部分魔法方法用于自定义在使用内建函数时类的行为。

基础方法

Python 中将对象转换为字符串有两个类似的魔法方法,即 __str__ 和 __repr__ 。

它两有什么区别呢?让我们先看结论:

  • __str__ 注重可读性,比如展示给用户。

  • __repr__ 注重明确性,比如展示给开发中的程序员。

举个栗子。假设有一个表示当前时间的类:

from datetime import datetimeclass MyDate:    def __init__(self):        self.date = datetime.now()f = MyDate()print(f)print(f.__repr__())print(f.__str__())# 输出:# <__main__.MyDate object at 0x000002510231AFA0># <__main__.MyDate object at 0x000002510231AFA0># <__main__.MyDate object at 0x000002510231AFA0>

打印的结果不明确,得不到我想要的跟时间有关的信息。

增加 __str__ 方法后:

from datetime import datetimeclass MyDate:    def __init__(self):        self.date = datetime.now()            def __str__(self):        return self.date.__str__()f = MyDate()print(f)print(f.__repr__())print(f.__str__())# 输出:# 2021-06-30 19:49:56.620427# <__main__.MyDate object at 0x00000251026C3CA0># 2021-06-30 19:49:56.620427

打印 f 或者 f.__str__() 均能够显示格式化后的时间信息,但是无法得知具体的类型。

如果只实现 __repr__ ,则有:

from datetime import datetimeclass MyDate:    def __init__(self):        self.date = datetime.now()        def __repr__(self):        return self.date.__repr__()f = MyDate()print(f)print(f.__repr__())print(f.__str__())# 输出:# datetime.datetime(2021, 6, 30, 19, 53, 14, 797960)# datetime.datetime(2021, 6, 30, 19, 53, 14, 797960)# datetime.datetime(2021, 6, 30, 19, 53, 14, 797960)

三种打印方式均被 __repr__ 覆盖,不仅显示了时间信息,也可得知具体的类型。

如果两种魔法方法同时实现:

from datetime import datetimeclass MyDate:    def __init__(self):        self.date = datetime.now()            def __str__(self):        return self.date.__str__()        def __repr__(self):        return self.date.__repr__()f = MyDate()print(f)print(f.__repr__())print(f.__str__())# 输出:# 2021-06-30 19:54:57.350076# datetime.datetime(2021, 6, 30, 19, 54, 57, 350076)# 2021-06-30 19:54:57.350076

总的来说两者中可优先实现 __repr__ ,有需要再实现 __str__。

此外,还有两个常用的方法 __dir__ 和 __dict__ 。

__dir__ 定义了调用 dir() 时的行为,返回对象的属性、方法的列表:

>>> a = 1>>> a.__dir__()['__repr__', '__hash__', '__getattribute__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__add__', '...']

而 __dict__ 则会输出所有实例属性组成的字典:

class Bar:    def __init__(self, a, b):        self.a = a        self.b = bb = Bar(1, 2)print(b.__dict__)# 输出:# {'a': 1, 'b': 2}

bytes 和 format 和 bool

理解了前面的内容,再来说类似的方法就简单了。

__bytes__ 方法实现了通过 bytes() 获取对象字节序列的表示形式。而 __format__ 方法被内置的 format() 或 str.format() 调用,获取对象的格式化后的字符串表示形式。

看例子:

from datetime import datetimeclass MyDate:    def __init__(self):        self.date = datetime.now()            def __bytes__(self):        return b'This is a bytes result'        def __format__(self, format_spec):        return 'The time is: ' + format(self.date, format_spec)f = MyDate()print(bytes(f))print(format(f, '%H:%M:%S'))# 输出:# b'This is a bytes result'# The time is: 10:15:36

而 __bool__ 就更简单了,它负责实现内置的 bool() 方法:

class Foo:    def __bool__(self):        return False    f = Foo()print(bool(f))# 输出:# False

如果类没有实现 __bool__ ,那么调用 bool() 会检查类的 __len__ ,非零则返回 True 。

如果连 __len__ 也没实现,则会直接返回 True 。

hash可哈希

__hash__ 这个稍微复杂点,放到最后来说。

Hash ,一般称作散列哈希

哈希算法是用来解决数据与数据之间对应关系的一种算法。它可以将任意长度的输入变换为固定长度的输出,该输出被称为哈希值。简单来说,就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。而实现了哈希算法的对象,就被称为可哈希的或者可散列的

Python 中的不可变类型通常都是可哈希的,比如数字、字母、字符串、元组等。

可变类型通常是不可哈希的,比如列表、字典、集合等。

Python 的三种数据结构:set 、 frozenset 和 dict 都是要求其键值是可哈希的,因为要保证键的唯一性。

如果自定义对象需要实现可哈希,那么就必须实现 __hash__ 方法。

我们自定义一个矢量类作为例子:

class Vector:    # 用于哈希算法的属性就像一个id    # 改变id会导致对象的身份混乱    # 因此将其标识为只能读取的私有变量    def __init__(self, x, y):        self.__x = x        self.__y = y            @property    def x(self):        return self.__x        @property    def y(self):        return self.__y        # 根据官方文档建议    # 哈希算法最好作用于输入值的元组上    # 以使得哈希值更加随机    def __hash__(self):        return hash((self.__x, self.__y))        # 实现__hash__ 必须同时实现 __eq__    def __eq__(self, other):        return self.__x == other.__x and self.__y == other.__y        # 格式化打印输出    def __repr__(self):        return f'(x: {self.__x}, y: {self.__y})'# 注意 v1 和 v2 的矢量值相同# 因此哈希函数计算结果也相同# 那么在集合中则会被归为同一个元素v1 = Vector(1,2)v2 = Vector(1,2)v3 = Vector(2,3)s = set([v1, v2, v3])print(s)# 输出:# {(x: 2, y: 3), (x: 1, y: 2)}

可以看到这个自定义的类实现了可哈希化,并且顺利的放到了集合 set 中。

需要注意的是,如果类实现了 __hash__ ,那么它也必须同时实现 __eq__ ,因为键的唯一性是由它两一起参与验证的。并且你还需要保证 x==y 和 hash(x) == hash(y) 是等效的。


本系列文章开源发布于 Github,传送门:Python魔法方法漫游指南

看完文章想吐槽?欢迎留言告诉我!

(0)

相关推荐

  • 反射,双下方法

    一. 反射 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力(自省).这一概念的提出很快引发了计算机科学领域关于应用反射性的研究.它首先被程序 ...

  • Python常用魔术方法

    什么是魔术方法? 在Python中,所有以双下划线__包起来的方法,统称为Magic Method(魔术方法),它是一种的特殊方法,普通方法需要调用,而魔术方法不需要调用就可以自动执行. 魔术方法在类 ...

  • 这有 73 个例子,彻底掌握 f-string 用法!

    英文:Miguel Brito,翻译:Python开发者 /  felixGuo26 在本文中,我将向你展示我认为对 Python 格式化字符串 f-string 来说最重要的一些技巧.你会通过各种样 ...

  • 使用纯 Python 代码来模拟实现 Python 字典

    在前面几篇文章中,我们一起了解了 Python 字典的概念.用法和实现原理. 今天,我们试着用 Python 代码来实现一个具有全功能的字典类,从而加强理解. [基本思路] 首先,我们要确认字典应具备 ...

  • Dji RoboMaster Tello SDK封装.3(回复指令解包类)

    老规矩,时间库+一个统计大类 初始化一些关于时间的的类变量 这里插一个Python列表的使用技巧,[:1]就是将当前列表的第一个元素丢掉 这个是类里面的所有的函数 我们这个函数倒着看,就是先看应用的地 ...

  • 温故而知新--day2

    温故而知新--day2 类 类与对象 类是一个抽象的概念,是指对现实生活中一类具有共同特征的事物的抽象.其实列化后称为对象.类里面由类属性组成,类属性可以分为数据属性和函数属性(函数属性又称为类方法) ...

  • Python学习:__repr__和__str__区别

    https://blog.csdn.net/nanhuaibeian/article/details/86694581 文章目录 一. `__repr__` 二. `__str__` 三.总结 一. ...

  • TelloPy-develop-0.7.0源码阅读.1

    最近我在反思,为什么我看了那么多书,为什么还是写不出大型的程序?我也很苦恼,我想了下.应该还是看的源码少的过,古人曾经说过熟读唐诗三百首,不会吟诗也会吟 .在读源码的选择上,我没有选择太复杂的开源库, ...

  • Python魔法方法漫游指南:描述符

    描述符是 Python 语言中一个强大的特性,它隐藏在编程语言的底层,为许多神奇的魔法提供了动力. 如果你认为它只是个花里胡哨.且不太能用到的高级主题,那么本文将帮助你了解为什么描述符是一个非常有意思 ...

  • Python魔法方法详解

    据说,Python 的对象天生拥有一些神奇的方法,它们总被双下划线所包围,他们是面向对象的 Python 的一切. 他们是可以给你的类增加魔力的特殊方法,如果你的对象实现(重载)了这些方法中的某一个, ...

  • Python中的魔法方法有哪些?

    公众号新增加了一个栏目,就是每天给大家解答一道Python常见的面试题,反正每天不贪多,一天一题,正好合适,只希望这个面试栏目,给那些正在准备面试的同学,提供一点点帮助! 小猿会从最基础的面试题开始, ...

  • 杭州品茶漫游指南,25个夏日打卡地标|ELLEDECO X Louis Vuitton City Guide

    在杭州旅行的奥义是放松心态.放缓脚步, 不必急于寻找风景, 风景自会印入眼帘. <ELLE DECORATION家居廊> 携手<路易威登城市指南>, 带你从茶开始,梳理茶与这座 ...

  • 一个非常好用的 Python 魔法库

    重磅干货,第一时间送达 本文转自:视学算法 来源:Be_melting https://blog.csdn.net/lys_828/article/details/106489371 1. 前言 在处 ...

  • 「备考」高考作文的审题方法之诗歌类材料

    诗歌材料 例:阅读下面一首小诗,根据要求写一篇不少于800字的文章:在枫叶上/露珠红红地闪烁/在荷花上/露珠有着泪滴似苍白的透明/-- 要求全面理解材料,但可以选择一个侧面.一个角度构思作文.自主确定 ...

  • 「备考」高考作文的审题方法之叙事类材料

    叙事类材料 把握所记叙的人.事所表现出的主旨 例:时下浮躁的娱乐行业流行一种所谓的"炒作",方法无奇不有,花样迭出,这种情况让人想起了挪威著名小提琴家欧雷·布尔的故事. 年轻的他在 ...

  • 改写句子方法汇总 10类句子练习丨可打印

    一.陈述句改反问句 修改技巧 1.修改的时候,开头加上"怎么.难道"-- 2.将肯定词改为否定词,也就是将句子中原来的:"是"改为"不是", ...

  • 「人体漫游指南」解锁健康养生新姿势~

    加班.熬夜.小烧烤. 脂肪肝.掉头发.血糖高 高节奏的生活和社会压力 让当代青年活在焦虑的循环里 长辈口中的「健康养生」也提前成为 年轻一代的社交高频词 年轻人到底该怎么挽救自己的健康? 腾讯视频和丁 ...