投稿007期|令人震惊到发指的PyObject对象代码设计之美
前言
最近在重温经典漫画《SlamDunk》的全国大赛篇,其中的一个情形可以很好的诠释虎躯一震这个状态——当樱木看到流川枫一次高难度投篮时内心的感受:“经过两万次射球练习后,樱木首次明白到流川枫这一球是相当厉害的,那正是他在两万次射球练习之中,经常在他脑海中出现的理想射球姿势”。


言归正传,其实对大多数程序开发人员来说,以上这个场景的感慨状态有时候也出现在我们看到经典代码的时候。最近正在思考关于Python语言的源生设计机制,有个问题不知道大家是否也有思考过:我们知道Python是由ANSI C实现的,在Python中一切都是对象的概念,但C并不是面向对象的语言,那么Python中的对象机制是如何实现的呢?带着这个疑问我研究了Python的源码,当我看到PyObject这个对象机制的核心结构体时我妥妥的震惊了,那么借着这期的主题就和大家分享一下PyObject对象基石的设计之美吧!
PyObject结构体介绍
通常来说,无论什么语言最终被计算机识别到的都是内存中的字节信息,那么对象实际上就是在更高的层次上把内存上的数据作为一个整体来考虑,这个整体可以是一个整数,可以是一个字符串,也就是我们所理解的对象。Python中所有的东西都是对象,它们拥有一些相同的内容,这些内容定义在PyObject这个结构体中,从Python源码文件object.h中可以找到它。
typedef struct _object {
PyObject_HEAD
} PyObject;
光看这个结构体可能还看不出什么高深的设计端倪,因为我们并不知道PyObject_HEAD是什么?源码中PyObject_HEAD是一个宏定义,定义了每一个Python对象所占内存的头部字节内容,那么我们把PyObject_HEAD这个宏定义替换成具体内容再直观的看下PyObject这个结构体。
typedef struct _object {
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
ob_refcnt是一个整形变量,它的作用是实现引用计数机制。比如一个对象A,当有一个新的PyObject 引用该对象时,A的引用计数增加;而当这个PyObject 被删除时,A的引用计数减少。当A的引用计数减少到0时,A就可以从堆上被删除,以释放出内存供别的对象使用。为什么是从堆上删除呢?因为Python中对象是在堆上申请的结构体,这点和C有很大的区别,C的变量是随函数创建,被压入栈中的。ob_type是一个指向_typeobject结构体的指针,这个结构体又是什么东西呢?实际上这个结构体也是一个对象,它是用来指定一个对象类型的类型对象,我们从源码中可以看出这个类型对象记录了不同的对象所需的内存空间的大小信息。那么简单的说,Python中对象机制的核心一个是引用计数,一个就是类型。
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;
cmpfunc tp_compare;
reprfunc tp_repr;
/* Method suites for standard classes */
PyNumberMethods *tp_as_number;
PySequenceMethods *tp_as_sequence;
PyMappingMethods *tp_as_mapping;
/* More standard operations (here for binary compatibility) */
hashfunc tp_hash;
ternaryfunc tp_call;
reprfunc tp_str;
getattrofunc tp_getattro;
setattrofunc tp_setattro;
/* Functions to access object as input/output buffer */
PyBufferProcs *tp_as_buffer;
/* Flags to define presence of optional/expanded features */
long tp_flags;
const char *tp_doc; /* Documentation string */
/* Assigned meaning in release 2.0 */
/* call function for all accessible objects */
traverseproc tp_traverse;
/* delete references to contained objects */
inquiry tp_clear;
/* Assigned meaning in release 2.1 */
/* rich comparisons */
richcmpfunc tp_richcompare;
/* weak reference enabler */
Py_ssize_t tp_weaklistoffset;
/* Added in release 2.2 */
/* Iterators */
getiterfunc tp_iter;
iternextfunc tp_iternext;
/* Attribute descriptor and subclassing stuff */
struct PyMethodDef *tp_methods;
struct PyMemberDef *tp_members;
struct PyGetSetDef *tp_getset;
struct _typeobject *tp_base;
PyObject *tp_dict;
descrgetfunc tp_descr_get;
descrsetfunc tp_descr_set;
Py_ssize_t tp_dictoffset;
initproc tp_init;
allocfunc tp_alloc;
newfunc tp_new;
freefunc tp_free; /* Low-level free-memory routine */
inquiry tp_is_gc; /* For PyObject_IS_GC */
PyObject *tp_bases;
PyObject *tp_mro; /* method resolution order */
PyObject *tp_cache;
PyObject *tp_subclasses;
PyObject *tp_weaklist;
destructor tp_del;
/* Type attribute cache version tag. Added in version 2.6 */
unsigned int tp_version_tag;
#ifdef COUNT_ALLOCS
/* these must be last and never explicitly initialized */
Py_ssize_t tp_allocs;
Py_ssize_t tp_frees;
Py_ssize_t tp_maxalloc;
struct _typeobject *tp_prev;
struct _typeobject *tp_next;
#endif
} PyTypeObject;
PyObject是一个定长对象的结构体,对于可变长度对象的结构体是PyVarObject,它比PyObject结构体多一个ob_size变量,用于指定容器中包含的元素数量。比如list中有5个元素,那么PyVarObject.ob_size的值就是5。PyVarObject实际上只是对PyObject的一个扩展而已,任何一个PyVarObject所占用的内存,开始部分的字节定义和PyObject是一样的。
动态类型的特性 厦门叉车哪家好 叉车价格是多少
我在《Python基础系列讲解—动态类型语言的特点》一文中讲到了Python作为动态类型语言的特点,变量不需要预先声明类型,当变量在赋值时解释器会根据值的类型创建对应的内存空间进行存储,并将变量指向这个地址空间。当时觉得这个机制太不可思议了,了解到PyObject这个结构体才发现原来是它的功劳。
比如Python创建一个整形对象PyIntObject,它会为这个对象分配内存,并进行初始化。然后这个对象会由一个PyObject变量来维护,因为每一个对象都拥有相同的对象头部,这使得对象的引用变得非常的统一。无论对象实际上的类型是什么,只需要通过PyObject指针就可以引用任意的一个对象。
总结
Python作为一门高级语言,其实大家也可以不必过多的推敲底层的实现机制,不过有一定程度的了解可以帮助我们更深入的理解和应用Python这门语言工具,甚至可以对解释器如何解释你的代码了如指掌,有助于排查语法层面的BUG。如果大家对Python底层的实现有浓厚的兴趣,这里推荐大家阅读《Python源码剖析》这本书。对了,我有电子版的,有需要的可以私信我。
投稿007期|令人震惊到发指的PyObject对象代码设计之美的更多相关文章
- 极客技术专题【007期】:jQuery初学者入门 - jQuery Event
日期:2013-8-19 来源:GBin1.com 技术专题介绍 专题:jQuery初学者入门[第三讲:jQuery Event] 分享人:极客标签技术编辑 -Lana (请站内关注分享人) 授课时 ...
- 安全意识第八期丨OMG!发个帖子竟然摊上大事了
互联网时代,话在网上说.钱在网上花.事在网上办,这早已成为一种习惯,越来越多的人也倾向于通过网络来获取信息. 借助现代信息技术,网络传播者通过即时通讯工具.微博.朋友圈等渠道发布信息,虽然传播起来更便 ...
- 《C专家编程》第四章——令人震惊的事实:数组和指针并不相同
数组和指针是C语言里相当重要的两部分内容,也是新手程序员最容易搞混的两个地方,本章我们锁定指针与数组,探讨它们的异同点. 首先来看指针与数组在声明上的区别: int a[10]; int *p; 很明 ...
- web 9个令人震惊的WebGL示例
20个使用WebGL和Three.js实现的网页场景 https://www.open-open.com/news/view/9d8136 20个使用WebGL和Three.js实现的网页场景 htt ...
- 2015年传智播客JavaEE 第168期就业班视频教程day45-ERP项目-01 10-类图结构分析设计
运行astah-pro.bat,这是windows下运行的.astah-run.sh是Linux下运行的. 类结构视图的作用是描述类模型和模型与模型之间的关系,也就是说我们在这要把这个一对多和多对多的 ...
- 发短信的主要代码(SmsManger)
SmsManager smsManager=SmsManager.getDefault(); smsManager.sendTextMessage(number,null,sms, null,null ...
- 剑指offer-高质量的代码
小结: 规范性:书写清晰.布局清晰.命名合理 完整性:完成基本功能.考虑边界条件.做好错误处理 鲁棒性:采取防御性编程.处理无效输入 面试这需要关注 输入参数的检查 错误处理和异常的方式(3种) 命名 ...
- AngularJS 指循环数组对象
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...
- 浅谈测试驱动开发(TDD)
测试驱动开发(TDD)是极限编程的重要特点,它以不断的测试推动代码的开发,既简化了代码,又保证了软件质量.本文从开发人员使用的角度,介绍了 TDD 优势.原理.过程.原则.测试技术.Tips 等方面. ...
随机推荐
- python基础整理7——爬虫——爬虫开发工具
HTTP代理神器Fiddler Fiddler是一款强大Web调试工具,它能记录所有客户端和服务器的HTTP请求. Fiddler启动的时候,默认IE的代理设为了127.0.0.1:8888,而其他浏 ...
- Python自动化之django orm之Q对象
Python自动化之django orm之Q对象 什么是Q对象? Encapsulates filters as objects that can then be combined logically ...
- mybatis提取<where><if>共用代码
mybatis项目dao层中很多sql语句都会拥有某些相同的查询条件,以<where><if test=""></if></where&g ...
- liunx 安装maven
cd /usr/local mkdir maven cd /usr/local/maven wget https://archive.apache.org/dist/maven/maven-3/3.1 ...
- Spring源码分析(十七)循环依赖
本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 实例化bean是一个非常复杂的过程,而其中比较难以理解的就是对循环依赖的解决, ...
- C++ - 类的虚函数\虚继承所占的空间
类的虚函数\虚继承所占的空间 本文地址: http://blog.csdn.net/caroline_wendy/article/details/24236469 char占用一个字节, 但不满足4的 ...
- CSS 学习路线(一)元素
元素(element) 类型:替换和非替换元素 替换元素(replaced element): 用来替换元素内容的部分并非由文档内容直接显示. eg:img input 非替换元素(nonreplac ...
- mysql 系统用户最大文件打开数限制
纸上得来终觉浅,绝知此事多宕机...记录一下自己很蠢的一次故障处理过程. 上周的时候,一个刚上线的系统又开始反映登不上了,因为最近这个系统也老是出现这个问题,开发也一直在找问题中,所以也没太在意.于是 ...
- PHP中查询一个日期是周几
PHP查询一个日期是周几 1.date('l'),获取的是英文的星期几.Sunday 到 Saturday date('l', strtotime('2019-4-6')); // Saturday ...
- Go语言中的常量
1 概述 常量,一经定义不可更改的量.功能角度看,当出现不需要被更改的数据时,应该使用常量进行存储,例如圆周率.从语法的角度看,使用常量可以保证数据,在整个运行期间内,不会被更改.例如当前处理器的架构 ...