欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 能源 > Python数据模型(如何使用特殊方法)

Python数据模型(如何使用特殊方法)

2025/3/31 21:52:23 来源:https://blog.csdn.net/weixin_42291376/article/details/146563946  浏览:    关键词:Python数据模型(如何使用特殊方法)

如何使用特殊方法

首先明确一点,特殊方法的存在是为了被 Python 解释器调用的,你自己
并不需要调用它们。也就是说没有 my_object.len() 这种写法,
而应该使用 len(my_object)。在执行 len(my_object) 的时候,如果
my_object 是一个自定义类的对象,那么 Python 会自己去调用其中由
你实现的 len 方法。

然而如果是 Python 内置的类型,比如列表(list)、字符串(str)、
字节序列(bytearray)等,那么 CPython 会抄个近路,len 实际
上会直接返回 PyVarObject 里的 ob_size 属性。PyVarObject 是表示
内存中长度可变的内置对象的 C 语言结构体。直接读取这个值比调用一
个方法要快很多。

很多时候,特殊方法的调用是隐式的,比如 for i in x: 这个语句,
背后其实用的是 iter(x),而这个函数的背后则是 x.iter() 方
法。当然前提是这个方法在 x 中被实现了。

通常你的代码无需直接使用特殊方法。除非有大量的元编程存在,直接
调用特殊方法的频率应该远远低于你去实现它们的次数。唯一的例外可
能是 init 方法,你的代码里可能经常会用到它,目的是在你自己
的子类的 init 方法中调用超类的构造器。

通过内置的函数(例如 len、iter、str,等等)来使用特殊方法是最
好的选择。这些内置函数不仅会调用特殊方法,通常还提供额外的好
处,而且对于内置的类来说,它们的速度更快。

不要自己想当然地随意添加特殊方法,比如 foo 之类的,因为虽
然现在这个名字没有被 Python 内部使用,以后就不一定了。

模拟数值类型

利用特殊方法,可以让自定义对象通过加号“+”(或是别的运算符)进
行运算。现在只是借用这个例子来展示特殊方法的使用。

我们来实现一个二维向量(vector)类,这里的向量就是欧几里得几何
中常用的概念,常在数学和物理中使用的那个(见图 1-1)。

image
图 1-1:一个二维向量加法的例子,Vector(2,4) + Vextor(2,1) =
Vector(4,5)

为了给这个类设计 API,我们先写个模拟的控制台会话来做 doctest。下
面这一段代码就是图 1-1 所示的向量加法:

>>> v1 = Vector(2, 4)
>>> v2 = Vector(2, 1)
>>> v1 + v2
Vector(4, 5)

注意其中的 + 运算符所得到的结果也是一个向量,而且结果能被控制台
友好地打印出来。

**abs 是一个内置函数,如果输入是整数或者浮点数,它返回的是输入值
的绝对值;如果输入是复数(complex number),那么返回这个复数的
模。**为了保持一致性,我们的 API 在碰到 abs 函数的时候,也应该返回
该向量的模:

>>> v = Vector(3, 4)
>>> abs(v)
5.0

我们还可以利用 * 运算符来实现向量的标量乘法(即向量与数的乘法,
得到的结果向量的方向与原向量一致 ,模变大):如果向量与负数相乘,得到的结果向量的方向与原向量相反。

>>> v * 3
Vector(9, 12)
>>> abs(v * 3)
15.0

示例 包含了一个 Vector 类的实现,上面提到的操作在代码里是用
这些特殊方法实现的:reprabsaddmul

from math import hypotclass Vector:def __init__(self,x=0,y=0):self.x=xself.y=ydef __repr__(self):return 'Vector(%r,%r)'%(self.x,self.y)def __abs__(self):return hypot(self.x,self.y)def __bool__(self):return bool(abs(self))def __add__(self, other):x=self.x+other.xy=self.y+other.yreturn Vector(x,y)def __mul__(self,scalar):return Vector(self.x*scalar,self.y*scalar)

虽然代码里有 6 个特殊方法,但这些方法(除了 init)并不会在
这个类自身的代码中使用。即便其他程序要使用这个类的这些方法,也
不会直接调用它们,就像我们在上面的控制台对话中看到的。上文也提
到过,一般只有 Python 的解释器会频繁地直接调用这些方法。接下来看
看每个特殊方法的实现。

字符串表示形式

Python 有一个内置的函数叫 repr,它能把一个对象用字符串的形式表
达出来以便辨认,这就是“字符串表示形式”。repr 就是通过__repr__这个特殊方法来得到一个对象的字符串表示形式的。如果没有实现
repr,当我们在控制台里打印一个向量的实例时,得到的字符串
可能会是 <Vector object at 0x10e100070>。

交互式控制台和调试程序(debugger)用 repr 函数来获取字符串表示
形式;在老的使用 % 符号的字符串格式中,这个函数返回的结果用来代
替 %r 所代表的对象;同样,str.format 函数所用到的新式字符串格
式化语法(https://docs.python.org/2/library/string.html#format-stringsyntax)
也是利用了 repr,才把 !r 字段变成字符串。

% 和 str.format 这两种格式化字符串的手段在本书中都会
使用。其实整个 Python 社区都在同时使用这两种方法。个人来讲,
我越来越喜欢 str.format 了,但是 Python 程序员更喜欢简单的
%。因此,这两种形式并存的情况还会持续下去。

在__repr__ 的实现中,我们用到了 %r 来获取对象各个属性的标准字符串表示形式——这是个好习惯,它暗示了一个关键:Vector(1, 2)
和 Vector(‘1’, ‘2’) 是不一样的,后者在我们的定义中会报错,因
为向量对象的构造函数只接受数值,不接受字符串

实际上,Vector 的构造函数接受字符串。而且,对于使用字符串构造的 Vector,这 6 个特
殊方法中,只有__abs__ 和__bool__ 会报错。

__repr__所返回的字符串应该准确、无歧义,并且尽可能表达出如何
用代码创建出这个被打印的对象。因此这里使用了类似调用对象构造器
的表达形式(比如 Vector(3, 4) 就是个例子)。
repr__和__str 的区别在于,后者是在 str() 函数被使用,或
是在用 print 函数打印一个对象的时候才被调用的,并且它返回的字
符串对终端用户更友好。

如果你只想实现这两个特殊方法中的一个,repr 是更好的选择,
因为如果一个对象没有__str__ 函数,而 Python 又需要调用它的时
候,解释器会用__repr__ 作为替代。

算术运算符

通过__add__ 和 mul,示例 1-2 为向量类带来了 + 和 * 这两个算
术运算符。值得注意的是,这两个方法的返回值都是新创建的向量对
象,被操作的两个向量(self 或 other)还是原封不动,代码里只是
读取了它们的值而已。中缀运算符的基本原则就是不改变操作对象,而
是产出一个新的值。

自定义的布尔值

尽管 Python 里有 bool 类型,但实际上任何对象都可以用于需要布尔值
的上下文中(比如 if 或 while 语句,或者 and、or 和 not 运算
符)。为了判定一个值 x 为真还是为假,Python 会调用 bool(x),这个
函数只能返回 True 或者 False。

默认情况下,我们自己定义的类的实例总被认为是真的,除非这个类对__bool__或者__len__ 函数有自己的实现。bool(x) 的背后是调用x.bool_()的结果;如果不存在__bool__方法,那么 bool(x) 会
尝试调用x.len()。若返回 0,则 bool 会返回 False;否则返回
True。

我们对__bool__ 的实现很简单,如果一个向量的模是 0,那么就返回
False,其他情况则返回 True。因为__bool__ 函数的返回类型应该是
布尔型,所以我们通过 bool(abs(self)) 把模值变成了布尔值。

如果想让 Vector.bool 更高效,可以采用这种实现:

def __bool__(self):
return bool(self.x or self.y)

它不那么易读,却能省掉从 abs 到__abs__ 到平方再到平方根这
些中间步骤。通过 bool 把返回类型显式转换为布尔值是为了符合__bool__ 对返回值的规定,因为 or 运算符可能会返回 x 或者 y
本身的值:若 x 的值等价于真,则 or 返回 x 的值;否则返回 y 的值。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词