『Python底层原理』--Python对象系统探秘

news/2025/2/23 0:20:50/文章来源:https://www.cnblogs.com/wang_yb/p/18730336

Python是一种非常灵活的编程语言,它的灵活性很大程度上来自于它的对象系统。

Python中,一切都是对象,这意味着无论是数字、字符串,还是我们自己定义的类的实例,它们在底层都遵循相同的规则。

本文尝试揭开Python的对象系统的神秘面纱。

1. 对象和类型

Python中,每个对象都有一个类型(type),而类型本身也是一个对象。

例如,int是整数的类型,str是字符串的类型,而我们自己定义的类(class)也是一种类型。

1.1. 对象 PyObject

CPython中,所有对象都基于一个底层的结构体PyObject

这个结构体定义了每个对象的基本属性,比如它的引用计数(用于垃圾回收)和它的类型指针。

PyObject的定义在源码中:Include/object.h中。

// _object 就是 PyObject 的别名
typedef struct _object { };

当我们创建一个整数对象时,它实际上是一个PyLongObject,它扩展了PyObject结构体:

// _longobject 是 PyLongObject 的别名
struct _longobject {PyObject_HEAD_PyLongValue long_value;
};

_longobject定义在源码文件:Include/cpython/longintrepr.h中。

Python代码中,我们可以通过type()函数查看一个对象的类型:

a = 42
print(type(a))  # <class 'int'>s = "hello"
print(type(s))  # <class 'str'>

1.2. 类型 PyTypeObject

类型(PyTypeObject)定义了对象的行为。

它的定义在源码:Doc/includes/typestruct.h文件中。

它的定义中包含了许多函数指针,这些函数指针决定了对象如何响应某些操作。

例如,tp_str函数指针定义了对象如何转换为字符串,

tp_as_number函数指针中则定义了加减乘除等等数值计算操作。

typedef struct _typeobject {PyObject_VAR_HEADconst char *tp_name; /* For printing, in format "<module>.<name>" *//* Method suites for standard classes */PyNumberMethods *tp_as_number;reprfunc tp_str; // 省略... ...
} PyTypeObject;

其中的PyNumberMethodsInclude/cpython/object.h中有定义。

包含了各种数值计算的函数指针。

typedef struct {/* Number implementations must check *both*arguments for proper type and implement the necessary conversionsin the slot functions themselves. */binaryfunc nb_add;binaryfunc nb_subtract;binaryfunc nb_multiply;// 省略... ...} PyNumberMethods;

也就是说,当一个类实现了这些函数指针,那么它的对象就能执行这些操作。

2. 类的特殊方法

Python中,我们经常使用特殊方法(如 __add____str__ 等)来定义对象的行为。

实际上,这些特殊方法与上面介绍的函数指针是相互关联的。

比如,类的__add__ 方法与上一节中PyTypeObject中的PyNumberMethods中的nb_add相对应,

__str__则与上面的tp_str相对应。

只要类实现了这些特殊方法(比如,__add____str__),那么,PyTypeObject中对应的函数指针就指向这些特殊方法的实现。

根据这个类创建的对象就有了相应的功能。

下面通过简单的例子来看看这个函数指针的作用。

首先,我们创建一个类,先不实现__add____str__

class MyClass:def __init__(self, n):self.n = n

然后根据这个类创建一个对象,并进行加法运算:

if __name__ == "__main__":obj = MyClass(10)obj + 1

运行结果:

果然,因为PyTypeObjectPyNumberMethods字段中的nb_add对应的函数指针没有实现,无法进行加法运算。

修改MyClass类,实现__add__方法。

class MyClass:def __init__(self, n):self.n = ndef __add__(self, m):self.n += mif __name__ == "__main__":obj = MyClass(10)obj + 1print(obj)

这时,MyClass的对象可以正常进行加法运算了,但是print出来的结果是对象的内存地址,我们希望看到对象中n的值。

再次修改MyClass,实现__str__方法,也就是对应PyTypeObjecttp_str对应的函数指针。

class MyClass:def __init__(self, n):self.n = ndef __add__(self, m):self.n += mdef __str__(self):return f"当前值: {self.n}"

这样,MyClass的对象既可以进行加法运算,也可以输出可读性较好的内容。

再补充一点,子类继承父类时,会自动继承父类中已经实现的特殊方法,比如:

class MyInt(int):passa = MyInt(10)
b = MyInt(20)
print(a + b)  # 输出: 30

MyInt继承了int类型的nb_addtp_str

3. 类型的创建

Python中,我们可以通过两种方式创建类型:静态定义和动态分配。

3.1. 静态定义

Python的内置类型(如intfloat)是通过静态定义的方式创建的。

它们的特殊方法直接指向具体的实现函数。

intfloat定义的源码分别在:Include/longobject.hInclude/floatobject.h

3.2. 动态分配

我们自己定义的类是通过动态分配的方式创建的。

当我们使用class语句定义一个类时,Python会自动为我们设置好所有必要的特殊方法。

例如:

class MyClass:def __str__(self):return "Hello from __str__!"obj = MyClass()
print(obj)  # 输出: Hello from __str__!

在这个例子中,MyClasstp_str函数指针被设置为一个函数,这个函数会调用我们的 _str__方法。

4. self和方法

Python类的定义中,方法是一种特殊的属性。

当我们通过实例调用方法时,Python会自动将实例作为第一个参数传递给方法。

class MyClass:def my_method(self):return "Hello from my_method!"obj = MyClass()
print(obj.my_method())  # 输出: Hello from my_method!

上面的例子中,my_method是一个函数,但它通过描述符协议变成了一个方法。

当我们通过obj.my_method()调用它时,self参数会自动被设置为obj

这里面提到的描述符协议Python中一个非常强大的机制,它允许对象控制对属性的访问(如获取、设置和删除)。

描述符Python中实现属性访问控制的基础,也是许多高级功能(如propertyclassmethodstaticmethod等)的底层实现原理。

描述符本质上就是一个包含以下方法的

  • __get__(self, instance, owner):用于访问属性时调用,instance是访问属性的实例,owner是该实例所属的类
  • __set__(self, instance, value):用于设置属性时调用
  • __delete__(self, instance):用于删除属性时调用

根据描述符协议,当通过对象(obj)访问my_method时,返回的是一个绑定方法,它会自动将实例对象作为self参数,这种行为是由__get__方法实现的。

5. 总结

Python对象系统的核心是PyObjectPyTypeObject

每个对象都有一个类型,而类型定义了对象的行为,特殊方法与CPython中的函数指针之间存在直接的映射关系。

Python会根据我们定义的特殊方法自动设置函数指针,同时,特殊方法也可以从父类继承。

通过理解这些概念,我们可以更好地理解Python的动态特性和灵活性。当我们使用class定义一个类时,可以想想背后发生的故事。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/887822.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MyBatis-Plus配置之 —— 配置冲突

MyBatis-Plus配置 在 SpringBoot 工程中,当使用 yml 文件为 MyBatis-Plus 添加配置时,可能会出现 “配置冲突” 的问题,即:配置项如下: # MyBatisPlus配置 mybatis-plus:configuration:map-underscore-to-camel-case: true #字段与属性,自动转换为驼峰命名# ...config…

TLV编码学习

TLV就是Tag+Length+Value Tag(1或者多字节): Tag首字节:Bit7:0表示tag只有1字节, 1表示tag是多字节Bit6表示通用编码tag还是私有编码tag解析第5bit为0/1分别表示Value字段按照基本类型编码或者TLV方式编码。 Bit0~4,如果 Tag Value小于31,直接放Tag Value,大于…

线性规划 - 对偶

线性规划 - 对偶定义 线性规划是一类满足限制条件为关于自变量的线性约束,且目标函数是关于自变量的线性函数的一类最优化问题。 对于一组自变量 \(x_i\),定义多元线性函数 \(f(x_1,x_2,\dots,x_n)=\sum\limits_{i=1}^na_ix_i\),称 \(f(x_1,x_2,\dots,x_n)\ge/\le/=b_i\) 为…

excel的index和counta函数结合用法解决将a,b列内容合并转到c列

Excel中将a,b列内容合并转到c列 如下图,a,b列各有一些连续的数据(但个数不确定),想要将整个a,b列内容合并转到c列,应该如何实现公式及分步说明 公式实现 =IF(ROW()<=COUNTA(A$1:A$100), A1, IF(ROW()<=COUNTA(A$1:A$100)+COUNTA(B$1:B$100), INDEX(B$1:B$100, ROW()…

1-3 S语言词法分析器设计

一、实验目的 了解词法分析程序的两种设计方法:根据状态转换图直接编程的方式; 利用DFA编写通用的词法分析程序。(选做)二、实验内容 1. 根据状态转换图直接编程 编写一个词法分析程序,它从左到右逐个字符的对源程序进行扫描,产生一个个的单词的二元式,形成二元式(记号…

【技术解读】【CloudSec】EmojiDeploy: Smile! Your Azure web service just got RCEd ._.

目录前言存在漏洞的服务 - Kudu SCM漏洞利用的关键点1、Same-site配置不当2、服务端对Origin的校验可被绕过3、利用一个高危功能接口3.1、DoS - POST /api/scm/clean、POST /api/app/restart3.2、RCE - POST /api/zipdeploy没校验一些自定义请求头 - 如:If-Match: *、X-Reques…

基于扩频解扩+turbo译码的16QAM图传通信系统matlab误码率仿真,扩频参数可设置

1.算法仿真效果 matlab2022a仿真结果如下(完整代码运行后无水印): 仿真操作步骤可参考程序配套的操作视频。2.算法涉及理论知识概要该通信系统主要用于图像传输,适用于对图像质量和传输可靠性要求较高的场景,如无人机图像传输、视频监控、无线电视广播等。在复杂的电磁环境…

【技术学习】【WebSec】GraphQL API vulnerabilities

目录Lab1:Accessing private GraphQL postsLab2:Accidental exposure of private GraphQL fieldsLab3:Finding a hidden GraphQL endpointLab4:Bypassing GraphQL brute force protectionsLab5:Performing CSRF exploits over GraphQLReference Lab1:Accessing private G…

通过MATLAB实现PID控制器,积分分离控制器以及滑模控制器

1.课题概述通过MATLAB实现PID控制器,积分分离控制器以及滑模控制器。通过对比三个算法可知,采用滑模控制算法,其具有最快的收敛性能,较强的鲁棒性,以及较小的超调量。其性能略优于基于积分分离的PID控制器,远优于PID控制器的控制性能。2.系统仿真结果 (完整程序运行后无水…

LDM/SDM

LDM: 把图像VAE进隐空间,Diffusion加噪+UNetCrossAttn去噪,再VAE解码到像素空间输出按照时间t,进行不同程度和类型的去噪,所以加入了time-embedding。 把加入了conditional和未加入conditional的图片线性融合,保证平滑最前面接入resnet,是为了更好地保留中低级特征如边缘…

【漏洞分析】【CTF】Wiz Kubernetes CTF(K8s LAN Party)Writeup

目录入口Challenge 1 - ReconChallenge 2 - Finding NeighboursChallenge 3 - Data Leakage后记Challenge 4 - Bypass BoundariesChallenge 5 - Lateral Movement小结Reference 入口 https://www.k8slanparty.com/ Challenge 1 - Recon这道题的目的是想让你找到隐藏在K8s集群内…

【技术解读】【WebSec】Abusing HTTP hop-by-hop request headers

Abusing HTTP hop-by-hop request headers 这个技术在 2019年入选了 Portswigger 安全社区评选的 top 10 Web hacking techiniques of 2019 的候选名单,尽管最终没有入选 top 10,但个人觉得还是挺有意思的,后来也出现了与之相关的真实世界的漏洞案例,因此值得学习。 什么是…