python机制_Python的类机制

2ff34e647e2e3cdfd8dca593e17d9b0a.png

文章目录

初识Python类机制

首先参照图(pic_01):

c50138e41a74319dd0ef0111b6369d88.png

我们可知Python在类机制的实现中引入了两个很纠结的概念:PyTypeObject和基类,从而引出了Class与Instance以及基类和子类的概念。

原以为这两个概念并不存在太大的区别,可在逐步了解Python的对象机制的过程中,我发现两者相辅相成,相互交织却在功能划分上又泾渭分明。

对于一套完整的类实现机制,其流程为:创建Class对象(PyTypeObject)

实例化对象来得到Instance(PyObject)

使用Instance

其中,Class对象对应了上图(pic_01)中的中间部分,Instance对象对应最右边,而在最左边还有一个 对象,该对象在创建Class对象的过程中起了至关重要的作用。所以图(pic_01)更多地向我们展示了类机制的is-instance-of关系,即instance是怎么一步步创建出来的。我觉得这很重要,它帮助我们理清了Python类机制的层次关系,解答了Python中的先有鸡还是先有蛋的问题。通过这样的叙述关系,我们知道了Instance对象是怎么来的,同样地,我们也弄清楚了负责创建Instance的Class对象又是怎么被创建。这对于我们了解Python的类机制怎么从无到有以及将Class与Instance的关系抽象出来并与其他语言的类概念联系起来,都非常有帮助。

但上面的描述不够具体,忽略了很多的实现细节。要想从根本上理解Python的对象机制,我们不仅要从设计上理解各个概念之间的关系,并且我们还需要从实现上窥视所谓的类和对象的本质。换种说法就是怎样用其他语言来实现Python的类机制。

类机制的实现

简单的说,Instance对象就是一堆数据以及围绕这些数据进行操作的一组方法的集合。在类机制里面,我们称数据之为成员变量,称围绕这些数据进行操作的方法为成员方法。

C++中的实现

在C++中,Instance对象无非就是一段固定的内存和一堆指令码的集合。内存用于存放数据,指令码用于操作内存中的数据以及一些逻辑操作。

在C++的世界里,Class仅仅是Instance的模板,它仅仅定义了Instance应该是什么样子的,有哪些成员变量,有哪些成员方法。这样纯粹的说明是不需要使用内存的(为了简化问题,这里不考虑static成员变量,话说static存放的内存空间也跟成员变量不一样=.=!!),所以在C++中,Class仅仅是一堆指令码的集合(指令码会从硬盘读取到内存,但因为概念不一样,所以区别于一般的存放程序数据的内存)。而Instance就不一样了,在Class的new函数(也是一堆指令码>.

总结一下就是,在C++的实现中:Class用于描述Instance的结构和方法,其本质是一堆指令码,包括用于操作Instance的结构的new、destroy等方法,以及提供给Instance使用的方法。

Instance是一段固定的内存和一堆指令码的集合,是面向对象的设计思想中的核心。其内存的申请和分配均由Class的new函数来负责,而成员方法均由Class来定义。脑洞大开一下,我再补充个观点,未得到验证所以不见得对,请大家绕道。

由于C++是对C的补充和扩展,所以它的实现也是依托于C。但是C本身是面向过程的语言,只有function和variable。套用我先前所提到的逻辑,Instance是一段固定的内存和一堆指令码的集合,慢着,怎么这么耳熟,这不就是variable(一段固定的内存)+ function(一堆指令码)的组合嘛!但是variable不能提前定义啊,否则它的内存地址就确定下来了,并且不同的Instance应该拥有属于自己的variable集合,这样的动态分配是Instance本身所不具备的功能,需要在更高层完成;同样地,我们不能重复定义function,所有的Instance应该share相同的function集合,再加上继承、共私有机制的引进,Instance本身无法实现这样的需求,同样需要在更高层完成。所以这时候我们需要Class来完成这些工作。Class通过new函数来为新创建的Instance申请空间,并在申请到的空间中申明variable;紧接着variable,Class会在之后的内存中存放Instance需要的函数所对应的函数指针。每个函数会根据所分配的首地址以及成员变量的地址偏移来得到variable并对其操作。大概这样就能实现了吧,么么哒~

Python中的实现

至于基类的继承,更多地是属性的重用,所以图(pic_01)并未很好地展现继承的层次关系。

以上是我对is-instance-of以及is-kind-of的初步理解。接下来,我将通过对Class对象以及instance对象创建过程的剖析来探究PyTypeObject。不可避免地,我们会涉及到类的继承,因为它们本来就都是整个Class对象的一部分。

图(pic_01)很好地展示了Python的类机制的层次关系,即metaclass负责创建其他的class对象,而这些被创建的class对象负责创建instance对象,所以也就出现了两层is-instance-of关系。也正是因为存在这两层的

is-instance-of,class对象和instance对象才能从无到有。我在阅读《Python源码剖析》的过程中,不曾看到哪个PyTypeObject的ob_type所指向的非metaclass,这样的机制极大地简化了Python的类机制,易于理解也易于实现。

为了更贴近Python的语言风格,在接下来的描述中,如果涉及到概念,我将延续先前使用的Class&Instance这样的术语,但在涉及到Python的具体实现中我将会用PyTypeObject来表示Class对象

用PyObject实例来表示Instance对象

用PyObject来泛指Python的所有对象(everything in python is a object)

用PyBaseObject_Type来表示所有Python最基本的Class对象,所有的PyObject均继承自它。原型为

为了更好地展示如何通过PyTypeObject来创建PyObject实例,我在这里展示另外一张图(pic_02):

e766085422b52a65e6f7429aa0d7552d.png

标上序号的虚线箭头代表了创建整数对象的函数调用流程,可以概括为:首先PyTypeObject(在这里即为PyInt_type)的tp_new会被调用,该函数可以视为new操作符(为instance对象申请内存空间以及初始化工作),若tp_new为NULL,PyTypeObject对象会去寻找基类(PyBaseObject_Type)的tp_new函数。因为所有的PyObject都继承自PyBaseObject_Type,而PyBaseObject_Type的tp_new的实现不为NULL,所以任何PyObject总能找到tp_new函数来完成用PyObject实例的创建。在具体的实现中,tp_new函数会访问PyTypeObject的tp_basicsize来确定要申请的空间大小;当空间申请结束之后,tp_new会调用tp_init函数来完成初始化工作。

Python的对象创建机制比较复杂,但工作原理却相当明朗,即Class对象调用自身的tp_new,赋以自身的一些必要属性作为参数,来为Instance对象分配内存和初始化工作。所以才有我先前的结论:Python是由Class对象(PyTypeObject)来负责对象的实例化,其中,我们看到某一Class对象的tp_new的具体实现可以不是由自身给出,而是顺藤摸瓜,通过一系列的函数指针的传递和函数调用来确定最终的函数实现。

在这里我们可以看到继承的运用。继承更多地

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值