【Python】第九章 其他

该文章内容整理自《Python编程:从入门到实践》、《流畅的Python》、以及网上各大博客

Python 底层原理

PyObject

Python中一切都是对象,全部的对象都有一个共同的基类。Python 是用 C 实现的,C 是一种 OO 的语言,而 Python 是一个 OOP 的语言,那么怎样在 C 语言层面实现 OOP ,实现多态,这是一个有意思的话题。Python 内部使用了一个 PyObject 结构体来保存全部对象共同的数据成员,以及实现GC机制所须要的一些辅助字段等,所以能够说 PyObject 就是 Python 对象机制的基石

PyObject 在 Include/object.h 中给出定义

typedef struct _object {
    _PyObject_HEAD_EXTRA
    Py_ssize_t ob_refcnt;
    struct _typeobject *ob_type;
} PyObject;

其中

  • _PyObject_HEAD_EXTRA:实现一个双向链表,但是只有在宏Py_TRACE_REFS被定义时,也就是Py_DEBUG被定义了,Python解释器Release版本是不会有它的,目标只是分析源码的话完全可以忽略
  • ob_refcnt:即object reference count,引用计数,和垃圾处理机制有关。当减为 0 时则会从堆上被删除(Python 中对象是在堆上申请的结构体)。而 Py_ssize_t 会根据64位系统或32位系统而调整格式,但本质上是 int
  • ob_type:指向一个 PyTypeObject,而 PyTypeObject 也是一个对象,用来指定一个对象类型的类型对象,记录了不同的对象所需的内存空间等大小信息。PyTypeObject 是元类的底层实现
typedef struct _typeobject {
    PyObject_VAR_HEAD
    const char *tp_name; /* For printing, in format "<module>.<name>" */
    Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */

    /* Methods to implement standard operations */

    destructor tp_dealloc;
    printfunc tp_print;
    getattrfunc tp_getattr;
    setattrfunc tp_setattr;
    PyAsyncMethods *tp_as_async; /* formerly known as tp_compare (Python 2)
                                    or tp_reserved (Python 3) */
    reprfunc tp_repr;
    ...
} PyTypeObject;

PyObject 内容相当的简洁,在 C 层面上就是一个结构体。PyObject 保存了每个对象都必须具备的东西,即引用计数和类型信息,但其余内容需要各自自行定义。每个Python对象内存的最前面的一块就放着PyObject,所以各种对象可以通过强制转换为PyObject来单独访问ob_refcnt以及ob_type属性。如 Python 创建一个整形对象 PyIntObject,为这个对象分配内存,并进行初始化。然后这个对象会由一个 PyObject 变量来维护,因为每一个对象都拥有相同的对象头部,这使得对象的引用变得非常的统一。无论对象实际上的类型是什么,只需要通过PyObject指针就可以引用任意的一个对象

PyObject 是一个定长对象的结构体,而变长对象的结构体则是 PyVarObject。PyVarObject 只比 PyObject 多一个 ob_size 属性,用来指明了变长对象中有多少个元素

typedef struct {
    PyObject ob_base;
    Py_ssize_t ob_size;
} PyVarObject;

存储机制

Python 通过三个方面进行内存管理

引用计数机制

Python内部使用引用计数来保持追踪内存中的对象,所有对象都有引用计数(即结构体 PyObject 中的 ob_refcnt)。sys.getrefcount(obj)函数可以获得对象的当前引用计数

引用计数增加的情况(注意是整数 4 的引用计数):

  1. 对象被创建,如 x=4
  2. 对象赋值给其他别名,如 y=x
  3. 作为容器对象的一个元素,如 a=[1,x,‘33’]
  4. 被作为参数传递给函数,如 foo(x)

引用计数减少的情况:

  1. 对象的别名被显式的销毁,如 del x
  2. 对象的别名被赋值给其他对象,如 x=5
  3. 引用超出作用域,,如上面的foo(x)函数结束时

引用计数机制是 Python 内存管理的主要方法。其优点是操作简单、实时。缺点则是维护性高,而且会出现死锁

# 互相引用时双方引用数都为1
a = [1,2]
b = [2,3]
a.append(b)
b.append(a)
DEL a
DEL b
垃圾回收机制

当一个对象的引用计数归零时,它将被垃圾收集机制处理掉。当垃圾回收启动时,Python 扫描到这个引用计数为0的对象,就将它所占据的内存清空

  1. 垃圾回收时,Python不能进行其它的任务,频繁的垃圾回收将大大降低Python的工作效率
  2. Python只会在特定条件下,自动启动垃圾回收(垃圾对象少就没必要回收)
  3. 当Python运行时,会记录其中分配对象(object allocation)和取消分配对象(object deallocation)的次数。当两者的差值高于某个阈值时,垃圾回收才会启动。可以使用 gc 模块里的 get_threshold() 函数来查看阈值,并且可以使用 gc 模块里的 collect() 函数来手动启动垃圾回收

另外,Python 将所有的对象分为0,1,2三代。所有的新建对象都是0代对象,当某一代对象经历过垃圾回收,依然存活,就被归入下一代对象。每10次0代垃圾回收,会配合1次1代的垃圾回收;而每10次1代的垃圾回收,才会有1次的2代垃圾回收

内存池机制

Python中以256K为界限划分大内存和小内存。其中,大内存使用malloc进行分配,小内存使用内存池进行分配

  1. 第 -1,-2层:操作系统进行操作
  2. 第0层:大内存。若请求分配的内存大于256K,malloc函
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值