慕体系Python全能工程师2024之深入底层原理

151 阅读4分钟

Python数据类型底层原理

Python是一种动态类型的编程语言,它允许我们在不指定类型的情况下为变量赋值。例如,我们可以连续地给一个变量赋值为整数和字符串,而不会报错。这种灵活性源于Python的动态数据类型设计,其中每个变量都包含了值信息和类型额外信息。

“夏のke”》 Ukoou·ㄷㅁΜ

在Python的内部实现中,每个对象都是基于C语言编写的结构体,例如整型对象。一个整型在Python中实际上是一个指向C语言结构体的指针。这个结构体定义如下:

struct _longobject {
    long ob_refcnt;
    PyTypeObject *ob_type;
    size_t ob_size;
    long ob_digit[1];
};

在这个结构体中,ob_refcnt是引用计数,用于协助Python的垃圾回收(GC)。ob_type是类型对象,它编码了变量的类型信息。ob_size表示数据成员的大小,而ob_digit则包含了实际的整数值。

由于Python的动态类型系统,存储一个整型需要额外的开销。除了实际的数值之外,还需要存储类型和引用计数等信息。这种设计虽然带来了灵活性,但也增加了内存的使用。

列表(list)是Python中的标准可变多元素容器。它的底层实现是一个包含多个Python对象的指针数组。列表的C语言结构体定义如下:

typedef struct {
    PyObject_VAR_HEAD
    PyObject *ob_item; // 指针数组
    Py_ssize_t allocated; // 申请内存的槽个数
} PyListObject;

列表中的每个元素都是一个指向Python对象的指针。当添加或删除元素时,可能需要重新分配内存。不过,由于Python的实现细节,并不是每次操作都需要改变数组的大小。

NumPy库提供了固定类型的数组,这与Python原生的列表有所不同。NumPy数组的底层结构体中包含了头信息、数据、维度和步幅等信息,然后指向数组的第一个元素。通过步幅和维度可以快速定位到数组中的任何元素。

慕课Python全能工程师2024 字典(dict)在Python中的底层实现是基于哈希表的。只有可哈希的对象才能作为字典的键。Python使用伪随机探测的哈希表作为字典的底层结构。解决哈希碰撞的方法包括开放寻址、再hash法、链地址法、公共溢出区以及装填因子等。

集合(set)的实现与哈希表类似,它基于hash对元素进行散列,只包含对键的引用,没有对值的引用。

通过深入了解Python数据类型的底层原理,我们可以更好地理解Python的内存管理和性能特性。这对于编写高效、可维护的Python代码至关重要。

Python封装底层实现原理

慕课Python全能工程师2024 Python中的封装特性并非通过传统的访问控制修饰符(如Java中的public、private等)来实现,而是通过名称修改(name mangling)的方式。这意味着,当你试图在一个类中定义私有属性或方法时,Python会自动将这些属性或方法的名称进行修改,以防止它们被外部直接访问。

在提供的示例代码中,我们定义了一个名为CLanguage的类,其中包含了私有属性__name__add,以及对应的setter和getter方法。此外,还有一个私有方法__display,用于打印这些私有属性的值。

class CLanguage :
    def setname(self, name):
        if len(name) < 3:
            raise ValueError('名称长度必须大于3!')
        self.__name = name

    def getname(self):
        return self.__name

    # 为 name 配置 setter 和 getter 方法
    name = property(getname, setname)

    def setadd(self, add):
        if add.startswith("http://"):
            self.__add = add
        else:
            raise ValueError('地址必须以 http:// 开头')

    def getadd(self):
        return self.__add

    # 为 add 配置 setter 和 getter 方法
    add = property(getadd, setadd)

    # 定义个私有方法
    def __display(self):
        print(self.__name,self.__add)

当我们尝试直接调用私有方法__display时,Python会抛出一个AttributeError,因为该方法的名称已经被修改,不再是__display。然而,如果我们知道了名称修改的规则,即在属性或方法名前加上_类名__,我们就可以访问到这些私有成员。例如,_CLanguage__display就是__display方法修改后的名称。

clang = CLanguage()
# 尝试调用私有的 display() 方法
clang.__display()  # 这将引发错误

# 调用name的setname()方法
clang.name = "新宝库"
# 调用add的setadd()方法
clang.add = "https://www.xinbaoku.com"
# 直接调用隐藏的display()方法
clang._CLanguage__display()  # 正确调用

此外,我们还可以访问和修改私有属性__name__add,尽管这不是推荐的做法,因为它破坏了封装性。

clang = CLanguage()
clang.name = "新宝库"
clang.add = "https://www.xinbaoku.com"
# 直接调用 name 和 add 私有属性
print(clang._CLanguage__name,clang._CLanguage__add)

# 甚至于,我们还可以通过这种方式修改 clang 对象的私有属性
clang._CLanguage__name = "Python教程"
clang._CLanguage__add = "https://www.xinbaoku.com/python"
print(clang._CLanguage__name,clang._CLanguage__add)