iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >python源码剖析之PyObject详解
  • 378
分享到

python源码剖析之PyObject详解

2024-04-02 19:04:59 378人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

目录一、python中的对象1.1 对象机制的基石PyObject二、类型对象2.1 对象的创建2.2 对象的行为2.3 类型的类型三、Python的多态性四、引用计数五、Pytho

一、Python中的对象

Python中一切皆是对象。
————Guido van Rossum(1989)

这句话只要你学过python,你就很有可能在你的Python学习之旅的前30分钟就已经见过了,但是这句话具体是什么意思呢?

一句话来说,就是面向对象中的“类”和“对象”在Python中都是对象。类似于int对象的类型对象,实现了“类的概念”,对类型对象“实例化”得到的实例对象实现了“对象”这个概念。

通常的说法是,对象是数据以及基于这些数据的操作的集合。在计算机上,一个对象实际上就是一片被分配的内存空间,这些内存可能是连续的,也有可能是离散的,这都不重要,重要的是这片内存在更高的层次上可以作为一个整体来考虑,这个整体就是一个对象。在这片内存中,存储着一系列的数据以及可以对这些数据进行修改或读取的一系列操作的代码。

在 Python 中,对象就是在堆上申请的结构体,对象不能是被静态初始化的,并且也不能是在栈空间上生存的。唯一的例外就是类型对象(type object),Python中所有的类型对象都是被静态初始化的。在 Python 中,一个对象一旦被创建,它在内存中的大小就是不变的了。 这就意味着那些需要容纳可变长度数据的对象只能在对象内维护一个指向一个可变大小的内存区域的指针。

1.1 对象机制的基石PyObject

PyObjectPyVarObject分别表示定长对象和变长对象,使用的C的struct实现的,在结构中分别只定义了 PyObject_HEADPyObject_VAR_HEAD,后者仅仅是前者加上一个表示容纳元素个数的ob_size


[object.h]

#define PyObject_HEAD \
	_PyObject_HEAD_EXTRA \
	int ob_refcnt; \
	struct _typeobject *ob_type;

#define PyObject_VAR_HEAD \
	PyObject_HEAD \
	int ob_size; 

而对于两者共有的PyObject_HEAD中,只有两个东西,一个是维护引用计数的ob_refcnt和一个指向类型对象PyTypeObject结构体的指针。因此, Python 中实际上对象机制的核心非常的简单,一个是引用计数,一个就是类型。并且Python中每一个对象的开始字节都是相同的头部,这使得对Python对象的引用十分统一,只需要一个PyObject*可以引用任意一个对象。

在这里插入图片描述

这两个结构体定义的只是Python中对象共有的部分,其他的具体类型会有额外的结构体来定义,否则的话所有的对象岂不是都一样了?比如int类型的结构体定义PyIntObject中包含了PyObject_HEADob_ival后者是一个long,存放具体的值。

二、类型对象

那初始化对象的时候,去那里获得对象的大小呢?只能是在类型对象PyTypeObject中了!类型对象中存放了大量对象的元信息,大小显然是一种和对象的类型有关的元信息!注意,一个PyObject对象就是Python中对面向对象理论中类这个概念的实现,这里面存放了类型名、内存空间、操作函数指针等信息。

2.1 对象的创建

Python会用两种方法创建对象,一种是泛型api(AOL:Abstract Object Layer),可以应用在任何Python对象上,API内不会有机制确定最终调用哪个具体函数,比如PyObject_New(PyObject, &PyInt_Type)。另一种是类型相关API(COL:Concrete Object Layer),只能应用于具体类型的对象上,比如PyInt_FromLong(10)

自定义对象在Python内部不可能存在COL,所以只能根据其类型对象来创建实例对象,这就需要PyTypeObject中的tp_new函数指针,如果是自定义对象,这个指针可能是空,那就通过PyTypeObjecttp_base找到类型对象的基类,再找tp_new指针,这个过程中会利用类型对象中记录的空间信息申请内存。对于 Python 中的任何一种变长对象,tp_itemsize 这个域是必须设置的,tp_itemsize 指明了由变长对象保存的元素的单位长度,所谓单位长度即是指一个对象在内存中的长度。这个 tp_itemsizeob_size 共同决定了应该额外申请的内存的总大小是多少。

内建对象最终会使用COL完成创建工作。

new函数完成后,会执行init函数,前者类似于new操作符,后者类似于构造函数。

在这里插入图片描述

2.2 对象的行为

像前面说的,对象的行为被类型对象中的函数指针所定义。这些操作中,有三组非常重要的操作族:tp_as_numbertp_as_sequencetp_as_mapping分别指向PyNumberMethodsPtSequenceMethodsPyMappingMethods函数族结构体。所谓“鸭子类型”,就行能找到该类型对应的操作,就可以当做这个类型来用。


class MyInt(int):
    def __getitem__(self, key):
        return key+str(self)

a=MyInt(1)
b=MyInt(2)
print(a+b)
print(a['somekey'])

可以发现通过int继承得到的数值对象,通过重写魔术方法,使其支持了字典类型的操作,其实我们可以认为是,制定了MyInt这个类型对象tp_as_mapping.mp_subscript操作。

2.3 类型的类型

之前说了,作为类的实现的类型对象也是Python对象,那么类型对象PyObjectob_type指针指向哪呢?是指向自己吗?尽管我一开始也是这么想的,但可惜这个答案不对,类型对象指向的对象是PyType_Type。这个对象在Python类型机制中很重要,所有用户自定义class的PyTypeObject对象都是通过这个对象创建的,因此他是Python中的元类(metaclass)。他是所有class的class。而元类的类型是自己,这里出现了我们一开始认为会出现的自己只想自己的情况!


i=1
class A:
    pass
a=A()
print(i.__class__) # 类型对象
print(i.__class__.__class__) # 元类
print(a.__class__) # 类型对象
print(a.__class__.__class__) # 元类
print(a.__class__.__class__.__class__) # 指向自己

在这里插入图片描述

留在这里的疑问:虚线和虚线指向的对象是啥玩?

三、Python的多态性

通过 PyObject 和类型对象, Python 利用 C 语言完成了 c++所提供的继承和多态的特性。一开始已经提到,Python中所有对象的前面几个字节都是PyObject类型也就是PyObject_HEAD结构体。因此在 Python 内部各个函数之间传递的都是一种范型指针PyObject*。这个指针所指的对象究竟是什么类型的,不知道,只能从指针所指对象的ob_type域判断,而正是通过这个域,Python 实现了多态机制。

真正执行的时候,通过找到实例对象指向的类型对象的函数指针来执行方法。这里同一个函数在不同情况下表现出了不同的行为,这正是多态的核心所在。

四、引用计数

在 Python 中,主要是通过Py_INCREF(op)Py_DECREF(op)两个宏来增加和减少一个对象的引用计数。当一个对象的引用计数减少到 0 之后, Py_DECREF将调用该对象的析构函数(deallocator function)(但是不一定真的释放该对象所占有的内存和系统资源),即类型对象中tp_dealloc指向的函数。例外的是类型对象,PyTypeObject是超越引用计数规则的,永远不会被析构,每一个对象中指向类型对象的指针不被视为对类型对象的引用。

这有些观察者模式(Observer)的影子,在ob_refcnt减为 0 之后,将触发对象销毁的事件;从 Python 的对象体系来看,各个对象又提供了不同的事件处理函数,而事件的注册动作正是在各个对象对应的类型对象中静态完成的。

PyObject中我们看到ob_refcnt是一个 32 位的整形变量,这实际是一个Python所做的假设,即对一个对象的引用不会超过一个整形变量的最大值。

五、Python对象的分类

在这里插入图片描述

到此这篇关于python源码剖析之PyObject详解的文章就介绍到这了,更多相关python源码PyObject内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: python源码剖析之PyObject详解

本文链接: https://www.lsjlt.com/news/126259.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • python源码剖析之PyObject详解
    目录一、Python中的对象1.1 对象机制的基石PyObject二、类型对象2.1 对象的创建2.2 对象的行为2.3 类型的类型三、Python的多态性四、引用计数五、Pytho...
    99+
    2024-04-02
  • python源码剖析之PyObject的示例分析
    这篇文章主要介绍python源码剖析之PyObject的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一、Python中的对象Python中一切皆是对象。————Guido van Rossum(1989)这...
    99+
    2023-06-15
  • Python源码学习之PyObject和PyTypeObject
    前言 Python是C语言实现的,因此Python对象在C语言层面应该是一个结构体 ,组织对象占用的内存。 不同类型的对象,数据及行为均可能不同,因此可以大胆猜测:不同类型的对象由不...
    99+
    2024-04-02
  • MyBatis源码剖析之Mapper代理方式详解
    目录源码剖析-getmapper()源码剖析-invoke()具体代码如下: //前三步都相同 InputStream resourceAsStream = Resources.ge...
    99+
    2024-04-02
  • C++11 shared_ptr 与 make_shared源码剖析详解
    目录0. 前言1. 源码分析1.1 头文件1.2 构造1.2.1 shared_ptr 的移动构造函数1.2.2 shared_ptr 的拷贝构造函数1.3 赋值重载1.4...
    99+
    2024-04-02
  • Spring源码剖析9:Spring事务源码剖析
    转自:http://www.linkedkeeper.com/detail/blog.actionbid=1045...
    99+
    2023-06-02
  • Vue解读之响应式原理源码剖析
    目录初始化initState()initProps()initData()observe()ObserverdefineReactive()依赖收集DepWatcher依赖收集过程移...
    99+
    2024-04-02
  • Java源码解析之详解ReentrantLock
    ReentrantLock ReentrantLock是一种可重入的互斥锁,它的行为和作用与关键字synchronized有些类似,在并发场景下可以让多个线程按照一定的顺序访问同一资...
    99+
    2024-04-02
  • Java源码解析之详解ImmutableMap
    一、案例场景 遇到过这样的场景,在定义一个static修饰的Map时,使用了大量的put()方法赋值,就类似这样—— public static final Map<St...
    99+
    2024-04-02
  • OpenJDK源码解析之System.out.println详解
    目录一、前戏二、JVM源码分析三、坑?四、总结一、前戏 可能不少小伙伴习惯在代码中使用sout打印一些信息,就像这样: System.out.println("hello wor...
    99+
    2024-04-02
  • Django admin源码剖析
    单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。 比如,某个服务器程序的配置信息存放在...
    99+
    2023-01-30
    源码 Django admin
  • vue3源码剖析之简单实现方法
    目录前言🍹准备工作🍲vue3用法🍖实现总结前言 最近,由于我的第一个vue3 + ts的正式项目,已经进入验收阶段。听你们老说vue3...
    99+
    2024-04-02
  • 如何剖析Python if语句源代码
    如何剖析Python if语句源代码,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。Python if语句在Python编程语言环境中有很多应用的地方,不少的程序员都不能够很顺利...
    99+
    2023-06-17
  • 微前端框架qiankun源码剖析之下篇
    目录引言四、沙箱隔离4.1 JS隔离1. Snapshot沙箱2. Legacy沙箱3. Proxy沙箱4.2 CSS隔离1. ShadowDOM2. Scoped CSS五、通信方...
    99+
    2023-02-09
    微前端框架qiankun剖析 微前端 qiankun
  • MyBatis源码剖析之Mapper代理方式细节
    MyBatis是一个流行的Java持久层框架,它提供了多种方式来执行数据库操作,其中之一就是通过Mapper代理方式。通过Mapper代理方式,开发者可以编写接口,然后MyBatis会动态地生成接口的实现类,从而避免了繁琐的SQL映射配置。...
    99+
    2023-08-16
    mybatis java 开发语言 原力计划
  • 微前端框架qiankun源码剖析之上篇
    目录引言一、single-spa简介二、qiankun简介三、子应用加载引言 注意: 受篇幅限制,本文中所粘贴的代码都是经过作者删减梳理后的,只为讲述qiankun框架原理而展示,...
    99+
    2023-02-09
    微前端框架qiankun剖析 微前端框架qiankun
  • Go并发之RWMutex的源码解析详解
    目录使用场景源码解析RWMutex结构体Lock()方法Unlock()方法RLock()方法RUnlock()方法RWMutex是一个支持并行读串行写的读写锁。RWMutex具有写...
    99+
    2023-03-15
    Go RWMutex源码 Go RWMutex
  • 深入剖析JavaReentrantLock的源码
    目录1. ReentrantLock的使用2. ReentrantLock类结构3. ReentrantLock源码解析3.1 ReentrantLock构造方法3.2 非公平锁源码...
    99+
    2022-11-21
    Java ReentrantLock源码 Java ReentrantLock
  • MyBatis3源码解析之如何获取数据源详解
    目录前言jdbc传统JDBC弊端思考源码分析获取数据源总结前言 上文讲的MyBatis部署运行且根据官网运行了一个demo:一步到位部署运行MyBatis3源码<保姆级>...
    99+
    2024-04-02
  • Vue源码分析之虚拟DOM详解
    为什么需要虚拟dom? 虚拟DOM就是为了解决浏览器性能问题而被设计出来的。例如,若一次操作中有10次更新DOM的动作,虚拟DOM不会立即操作DOM,而是将这10次更新的diff内...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作