##字符串表示形式
__repr__
特殊方法被内置为得到对象的字符串表示形式的repr调用。如果我们没有实现__repr__
,向量实例在控制台显示就会如 <Vector object at 0x10e100070>
形式。
表达式求值的结果,交互式控制台和调试器会调用repr,正如% r
占位符在典型情况下的格式是%运算符,!r
转换符在新的格式字符串语法用于str.format方法。
请注意,在我们的__repr__
实现中我们使用%r获得属性的标准表示来显示。这是很好的做法,因为它显示矢量Vector(1, 2)
和矢量Vector("1"、"2")
间的关键差异--后者不会在这个例子中工作,因为构造函数的参数必须是数字,而不是字符串
通过__repr__
返回的字符串应该是明确的,如果可能的话,匹配所必需的源代码,以重新创建所代表的对象。这就是为什么我们选择的代表看起来像调用类的构造函数:例如Vector(3, 4)。
对比__repr__
与__str__
,__str__
是通过str()
构造函数隐式由打印功能使用的。__str__
应该返回一个适合的字符串显示给用户。
如果你只能实现其中的一个特殊方法,选择__repr__
,因为当没有自定义__str__
可用时,Python调用__repr__
作为备用方案。
###tips
__repr__
与__str__
之间的区别:是由Pythonistas Alex Martelli和Martijn Pieters优秀贡献的StackOverflow问题
##算数操作符
例1-2实现了两个操作符:+和*,展现__add__
和__mul__
的基本用法。请注意,在这两种情况下,该方法创建并返回一个新的实例Vector,不修改任何一个操作数-本身或其他操作数仅仅是读取。这是中缀运算符的预期行为:创建新的对象并没有碰到其操作数。在第13章中,我将有更多的要说。
###tips
作为实现,例1-2允许数乘以一个向量的,但不允许由一个向量乘以一个数,它违反了乘法的交换属性。我们将在13章,用特殊方法__rmul__
修补这个问题。
##自定义类型的布尔值 虽然Python有一个bool类型,它却接受一个布尔上下文中的任何对象,如表达控制的if或者while语句,或作为操作数来进行与、或、否操作。Python应用bool(x)确定一个x值是真的或是假的,它总是返回true或false。
默认情况下,除非__bool__
或__len__
被实现,用户定义的类的实例被默认认为是真的。基本上,bool(x)调用x.__bool__()
和使用其结果。如果__bool__
不实现,Python试图调用x.__len__()
,如果x.__len__()
返回零,则返回false。否则,返回true。
我们的实现__bool__
的思路很简单:如果向量的大小为零,它返回false,否则,返回true。我们使用bool(abs(self))
将大小转换为布尔因为__bool__
预计将返回一个布尔值。
注意特殊方法__bool__
是如何允许你的对象是与在建在Python标准库的文档类型定义真值章检验规则一致的。
##特殊方法概述
Python语言参考的数据模型列表列出83特殊的方法名,其中47个用于实现算术,位和比较运算符,作为什么是可用的概述,见下表一和下表二
##为何len不是一个方法
这个问题2013年我问核心开发者Raymond Hettinger,他的答案的关键可以用Python之禅的一句话来概括,在纯粹性和实用性之间是倾向实用性的。
在“特殊的方法”第8页描述了为何当x是一个内置类型的一个实例时len(x)跑得很快。没有方法由CPython的内置对象调用:长度仅仅是从C结构体的一个域读取。获取集合中的项的数量是一种常见的操作,必须有效地为字符串,列表,memoryview等基本和多样类型工作。
换句话说,len是不能称其为方法,因为它是作为Python数据模型的一部分,就像abs得到特殊待遇的。但多亏了特殊的方法__len__
你也可以用你自己的自定义对象让len工作。这是在内置对象需要高效和语言的一致性之间的公平折衷。再从Python之禅引入一句话来概括:“特殊情况不足以打破规则。”
##本章小结
通过实现特殊的方法,你的对象可以像内置类型一样动作,使得大家公认为是Pythonic的有表现力的编码风格。
对于Python对象的基本要求是提供可用字符串表示自身,一是用于调试和记录,另外是用于呈现给最终用户。这就是为什么特殊方法__repr__
和__str__
在数据模型中存在。