返回

集合类型深入剖析:揭秘Python虚拟机中的实现机制

后端

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 的源代码相对简单,易于理解。