[Python] 魔法方法

您所在的位置:网站首页 Python中len函数应用 [Python] 魔法方法

[Python] 魔法方法

2024-07-15 17:14| 来源: 网络整理| 查看: 265

[Python] 魔法方法__len__的底层实现 1 魔法方法__len__的介绍1.1 魔法方法介绍1.2 例子1.2 `__len__()`方法的介绍 2 `__len__()`的实现源码解读2.1 Objects的实现2.2 `__len__()`方法实现 3 总结

1 魔法方法__len__的介绍 1.1 魔法方法介绍

Python中的魔法方法是指以双下划线__开头和结尾的特殊方法,也称为"dunder methods"(double underscore methods)。这些方法允许你自定义类的行为,使其具有类似于内置类型的行为,例如加法、比较等。它们通常被称为Python的特殊方法,因为它们有着特殊的功能和语法。

以下是一些常见的Python魔法方法及其用途的例子:

__init__(self, ...): 初始化对象,在创建对象时调用该方法。 __str__(self): 返回对象的字符串表示,使用print()函数时会调用该方法。 __repr__(self): 返回对象的"official"字符串表示,通常用于调试和开发目的。 __len__(self): 返回对象的长度,通常用于支持内置len()函数。 __getitem__(self, key): 定义对象的索引操作,使其支持类似列表和字典的索引访问。 __setitem__(self, key, value): 定义对象的索引赋值操作。 __delitem__(self, key): 定义对象的删除操作。 __add__(self, other): 定义对象的加法操作,使其支持+运算符。 __sub__(self, other): 定义对象的减法操作,使其支持-运算符。 __eq__(self, other): 定义对象的相等比较操作,使其支持==运算符。 __lt__(self, other): 定义对象的小于比较操作,使其支持运算符。 __call__(self, ...): 让对象可以像函数一样被调用。

1.2 例子 class Vector: def __init__(self, x, y): self.x = x self.y = y def __str__(self): return f"Vector({self.x}, {self.y})" def __add__(self, other): if isinstance(other, Vector): return Vector(self.x + other.x, self.y + other.y) else: raise TypeError("Unsupported operand type for +") def __eq__(self, other): if isinstance(other, Vector): return self.x == other.x and self.y == other.y return False # 创建两个Vector对象 v1 = Vector(1, 2) v2 = Vector(3, 4) # 使用自定义的魔法方法进行加法操作 v3 = v1 + v2 # 使用自定义的魔法方法进行相等比较 if v1 == v2: print("v1 and v2 are equal") else: print("v1 and v2 are not equal") print(v3) # 输出:Vector(4, 6) 1.2 __len__()方法的介绍

该方法的时间复杂度为O(1),也就是说,它的性能是非常高的。当你调用内置的len()函数并传递一个对象作为参数时,Python会尝试调用该对象的__len__()方法来获取其长度。这使得自定义的对象可以像内置的容器对象(例如列表、元组、字符串等)一样支持len()函数。

__len__()方法在你创建的类中可以根据需要进行定义,它应该返回一个表示对象长度的整数值。如果未定义__len__()方法,当尝试使用len()函数获取对象的长度时,会抛出TypeError异常。

2 __len__()的实现源码解读 2.1 Objects的实现

设计到底层的方法,由于对象和方法都是内建的,并不是由Python实现的,我们就不得不看一下它的C语言实现,所以我们先打开Objects的实现,如下,该定义可以在cpython/Include /object.h中找到。

typedef struct { PyObject ob_base; Py_ssize_t ob_size; /* Number of items in variable part */ } PyVarObject;

可以看到,在Objects中,存在一个ob_size,用于对象的大小,我们在后面会发现__len__()和它的关系。

2.2 __len__()方法实现

该方法的实现可以在cpython/Python /bltinmodule.c中找到,如下

/*[clinic input] len as builtin_len obj: object / Return the number of items in a container. [clinic start generated code]*/ static PyObject * builtin_len(PyObject *module, PyObject *obj) /*[clinic end generated code: output=fa7a270d314dfb6c input=bc55598da9e9c9b5]*/ { Py_ssize_t res; res = PyObject_Size(obj); if (res tp_as_sequence; // 指针不为空 if (m && m->sq_length) { // 获取长度 Py_ssize_t len = m->sq_length(o); // 确保长度是非负的 assert(_Py_CheckSlotResult(o, "__len__", len >= 0)); return len; } return PyMapping_Size(o); } 该函数主要是获取了指向对象头的指针,如果它的长度是合理的,就返回,如果对象不是序列对象(即没有定义sq_length方法),则会调用PyMapping_Size(o)来获取映射对象的大小。这个函数用于获取映射对象(例如字典)的键值对数量。判断返回的结果,如果长度为非负,则转成long类型返回,否则报错并返回空。以list为例,我们可以看到 static PySequenceMethods list_as_sequence = { (lenfunc)list_length, /* sq_length */ (binaryfunc)list_concat, /* sq_concat */ (ssizeargfunc)list_repeat, /* sq_repeat */ (ssizeargfunc)list_item, /* sq_item */ 0, /* sq_slice */ (ssizeobjargproc)list_ass_item, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)list_contains, /* sq_contains */ (binaryfunc)list_inplace_concat, /* sq_inplace_concat */ (ssizeargfunc)list_inplace_repeat, /* sq_inplace_repeat */ }; list的长度和list_length函数进行了关联,其定义如下 static Py_ssize_t list_length(PyListObject *a) { return Py_SIZE(a); } 而Py_SIZE则返回了PyObject当中的ob_size。 3 总结

从该__len__()方法,理解了为什么内建的方法可以做到高效,不仅是逻辑简单,更因为直接是C语言,效率更高。此外,也学习了如何查询Python内建函数对应的C语言实现,帮助我们理解和感叹Python语言设计的细节,对于其他函数可使用同样的方式学习了解。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3