集合类型深入剖析:揭秘Python虚拟机中的实现机制
2024-02-14 13:42:36
Python 虚拟机中的集合 set 是一个无序的可变容器,它可以包含任何类型的元素,但不能包含重复的元素。在本文中,我们将介绍 Python 虚拟机中集合 set 的实现原理,并对源代码进行剖析。
哈希表
集合 set 的底层实现是哈希表。哈希表是一种数据结构,它使用哈希函数将键值对映射到数组中的位置。当查找一个键时,哈希函数会计算键的哈希值,然后使用该哈希值来确定该键值对在数组中的位置。哈希函数的设计非常重要,它必须能够均匀地将键值对映射到数组中,以避免哈希冲突。
哈希函数
Python 虚拟机中使用了一个简单的哈希函数来计算键的哈希值。该哈希函数将键转换为一个整数,然后对该整数取模得到哈希值。哈希值是一个非负整数,它决定了键值对在数组中的位置。
哈希冲突
哈希函数有时会将不同的键映射到同一个哈希值,这种情况称为哈希冲突。当发生哈希冲突时,Python 虚拟机使用链表来解决冲突。链表是一种数据结构,它将具有相同哈希值的键值对连接起来。当查找一个键时,Python 虚拟机会遍历链表,直到找到具有相同键的键值对。
源代码剖析
集合 set 的源代码位于 Python 虚拟机中 Objects/setobject.c
文件中。该文件包含了集合 set 的数据结构定义、函数实现以及单元测试代码。
集合 set 的数据结构定义如下:
typedef struct {
PyObject_HEAD
Py_ssize_t ob_size; /* number of elements */
int ob_hash; /* hash value -- for use by set.update() */
hashtable ht;
} PySetObject;
该数据结构包含了集合 set 的大小、哈希值以及哈希表。哈希表是一个数组,它包含了键值对。每个键值对由一个键和一个值组成。键是一个 Python 对象,值是一个 PyObject 指针。
集合 set 的函数实现如下:
static PyObject *
set_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PySetObject *so;
Py_ssize_t ninargs;
ninargs = PyTuple_Size(args);
if (ninargs > 0) {
return _set_new(type, args, kwds);
}
so = PyObject_GC_New(PySetObject, type);
if (so == NULL)
return NULL;
so->ob_size = 0;
so->ob_hash = -1;
_Py_hashtable_init(&so->ht, _PySet_hash_impl, _PySet_compare_impl);
return (PyObject *)so;
}
该函数创建了一个新的集合 set 对象。它首先检查参数的个数,如果参数个数大于 0,则调用 _set_new()
函数来创建集合 set 对象。如果参数个数为 0,则创建一个空集合 set 对象。
static PyObject *
set_update(PySetObject *set, PyObject *other)
{
Py_ssize_t i;
Py_hash_t hash;
for (i = 0; i < other->ob_size; i++) {
PyObject *value = other->ob_item[i];
PyObject *weakref = PyObject_GetWeakrefKey(value);
hash = PyObject_Hash(value);
if (hash == -1) {
PyErr_Clear();
continue;
}
_Py_hashtable_set(&set->ht, hash, value, weakref);
}
return Py_None;
}
该函数将另一个集合 set 的元素添加到当前集合 set 中。它首先遍历另一个集合 set 的元素,然后对每个元素计算哈希值。然后,它使用哈希值将元素添加到当前集合 set 的哈希表中。
static PyObject *
set_contains(PySetObject *set, PyObject *key)
{
Py_hash_t hash;
hash = PyObject_Hash(key);
if (hash == -1) {
PyErr_Clear();
return Py_False;
}
return PyBool_FromLong(_Py_hashtable_get(&set->ht, hash, key) != NULL);
}
该函数检查当前集合 set 是否包含某个元素。它首先计算元素的哈希值,然后使用哈希值在当前集合 set 的哈希表中查找元素。如果找到元素,则返回 True,否则返回 False。
单元测试代码
Python 虚拟机中提供了单元测试代码来测试集合 set 的实现。这些单元测试代码位于 Tests/set.test
文件中。
import unittest
import sys
class SetTests(unittest.TestCase):
def test_init(self):
s = set()
self.assertEqual(len(s), 0)
def test_add(self):
s = set()
s.add(1)
self.assertEqual(len(s), 1)
s.add(2)
self.assertEqual(len(s), 2)
def test_remove(self):
s = set([1, 2, 3])
s.remove(2)
self.assertEqual(len(s), 2)
def test_contains(self):
s = set([1, 2, 3])
self.assertTrue(1 in s)
self.assertFalse(4 in s)
def test_update(self):
s = set([1, 2, 3])
s.update([4, 5, 6])
self.assertEqual(len(s), 6)
def test_intersection(self):
s1 = set([1, 2, 3])
s2 = set([3, 4, 5])
s3 = s1.intersection(s2)
self.assertEqual(len(s3), 1)
self.assertTrue(3 in s3)
def test_union(self):
s1 = set([1, 2, 3])
s2 = set([3, 4, 5])
s3 = s1.union(s2)
self.assertEqual(len(s3), 5)
self.assertTrue(1 in s3)
self.assertTrue(2 in s3)
self.assertTrue(3 in s3)
self.assertTrue(4 in s3)
self.assertTrue(5 in s3)
def test_difference(self):
s1 = set([1, 2, 3])
s2 = set([3, 4, 5])
s3 = s1.difference(s2)
self.assertEqual(len(s3), 2)
self.assertTrue(1 in s3)
self.assertTrue(2 in s3)
def test_symmetric_difference(self):
s1 = set([1, 2, 3])
s2 = set([3, 4, 5])
s3 = s1.symmetric_difference(s2)
self.assertEqual(len(s3), 4)
self.assertTrue(1 in s3)
self.assertTrue(2 in s3)
self.assertTrue(4 in s3)
self.assertTrue(5 in s3)
if __name__ == '__main__':
unittest.main()
这些单元测试代码测试了集合 set 的各种操作,包括初始化、添加元素、删除元素、查找元素、更新集合、求交集、求并集、求差集以及求对称差集。
结论
集合 set 是 Python 虚拟机中一种重要的数据结构。它使用哈希表来实现,哈希表可以快速地查找元素。集合 set 的源代码相对简单,易于理解。