python里object是什么类型_Python构件:PyObject

原标题:Python构件:PyObject

147091807428184751.GIF

Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发。

各位读者,你们好!

深入使用Python语言之前,我们有必要熟悉其中的主要概念。十分简单,每个元素都是一个对象。这是我们学习Python内部原理的第一步,以便我们继续深入学习。

本文主要探讨的是如何在低版本Python系统中操作它的对象。接下来要讨论的CPython操作基于Python 2.7.8。

在这里,我假定你们已经下载好了Python源码并已将其解压,如此,相关的源代码将能在系统跟文件夹下找到。

PyObject & PyVarObject

Python中的所有元素都是对象。从字面意思理解,你在Python中操作的任何元素都是一个由C语言实现的PyObject对象。

函数

切片

文件

迭代器

描述符

sequence对象

数值类型

值得一提的是,当你使用C语言结构原型时,Python对象在内部由PyObject和PyVarObject表示,其中PyVarObject是一种泛型对象。后者适用于各种可变尺寸的容器对象(动态的),而前者适用于其他对象(静态的)。

只要每个内建或是由用户指定的类型被封装成一个对象,就可以利用辅助信息进行移植。Python也不例外,每个Python对象都有一个指向类型对象的指针和一个引用计数器。虽然方便,但也需要付出相应的代价。直接的后果是导致性能下降。不过好在Python使用的一些技术和算法为了提高处理速度允许进行相应的改良,如字符串驻留算法和自适应数乘算法等。

下面的内容引用自Python2.7.10官方文档:

PyObject

所有对象均衍生自这种基类型,这种类型包含如何将指向对象的指针亦作为对象处理的信息,这正是所Python需要的。在正常的“release”编译方式下,它仅包含对象的引用计数值以及相应指向类型对象的指针。这和由宏指令PyObject_HEAD定义的类相一致。

PyVarObject

它由PyObject扩展而来,另外增加了ob_size类,仅用于那些有长度概念的对象。这种类型在Python/C应用中不太常见。它和由宏指令PyObject_HEAD定义的类也是一致的。

不要担心不会宏指令或者不知道变量名,我们将会实现它们。

PyObject和PyVarObject结构究竟是什么呢?这里从源码中摘录了一个片段:

..includeobject.h

147091807439214020.PNG

上述代码并不完全,我们继续探究(为了代码的清晰易懂,相较原文有所删减):

..includeobject.h

147091807766476944.JPEG

有些宏用于定义类,其他的用于初始化。

不知道你有注意到 _PyObject_HEAD_EXTRA 和 _PyObect_EXTRA_INIT 宏并没有定义了吗?事实上,全部的Python版本都是默认这样处理的。定义它们的唯一方式是使用Python编译器“Debug”一下。当然,这将会在以后进行介绍。作为教育目的,它们将以未定义的方式给出。

全部的宏都展开后,PyObject将是这个样子:

147091807850507239.PNG

不必担心Py_ssize类型,把它想成int型就行。其他类的含义可以从词义看出来:ob_recnt为引用计数器,ob_type则是指向PyTypeObject的指针。这点稍后再谈。

现在看看PyVarObject源码吧:

147091807889001122.PNG

可以看出来,大部分内容和PyObject是相同的,除了一个例外:ob_size,它表示可变尺寸容器所包含项目的数量(俏皮话)。

面向对象的C程序设计

至此,我想你应该明白为什么PyObject和PyVarObject(以及我们稍后将会学到的其他对象)表现出类似的特性了吧(ob_refcnt和ob_type)?

这些特性使得我们能够从底层类型抽象出共同的概念,并使我们能够以类似的方式使用各种类型,不管是简单的整形或字符串型,还是类实例,抑或切片对象。

每个Python的类型(PyIntObject,PyFloatObject以及PyDictObject)运行时都会将PyObject_HEAD作为第一个参数(或者第一个参数列表中的第一个参数,以此类推)。子对象成员的位置会被确保和全对象的位置一致。

PyObject_HEAD通常在子对象成员中出现,但只要ob_type被用于获取全部类型信息,它就能够变身为全部类型了。这种技术为C语言增色不少,尤其在轻量继承方面。

PyIntObject & PyDictObject

让我们看看实体对象PyIntObject和PyDictObject在Python中是如何运行的。

..includeintobject.h

147091807988740758.PNG

..includeintobject.h

147091808002338626.PNG

又看到PyObject_HEAD了吧?这意味着我们可以将PyIntObject看成PyObject附加了一些数据(上例中为long类型)。

同理可以查看PyDictObject对象(Python中的符号“{}”代表字典):

147091808050897826.PNG

虽然字典类型稍微复杂一点,但我们仍可将它看做PyObject对象,就像将PyIntObject看成PyObject一样。仅仅的不同是PyDictObject增加了更多的数据。最为重要的是,这些对象被严格地定义在PyObject_HEAD之后。

学习了PyObject_HEAD的内部原理后,我们还得知道Python中的Py*Object对象,下面的代码段中的注释足以帮助你理解它的运行原理。它向你展示了Python如何界定当前所处理的类型:

147091808101860849.PNG

为了增加代码的可读性,Python定义了大量的宏。例如,你可以使用PyInt_Check或者PyInt_CheckExact代替ob_type。类似的宏定义在每个Python对象的C文件开头找到。

字典对象“{}”:PyDict_Check和PyDict_CheckExact

函数对象:PyFunction_Check

元组对象“()”:PyTuple_Check和PyTuple_CheckExact

到现在,上面的代码就可以重写为这样了:

147091808123912209.PNG

一些Python对象同时拥有自己的专有类型检查功能和普通类型检查功能。它们的定义在..IncludeObject.h文件中,例如:

147091808148767326.PNG

PyTypeObject

到现在,关于PyObject未曾提及的就剩类型了。Python中的类型不仅仅是个名称(如“int”和“tuple”)和相应存储的值,还包括大量东西(例如函数,数据成员),它们若被联系到一起,将会产生一系列特性。

回忆一下PyObject_HEAD代码段:

147091808172666519.PNG

其中的ob_type涉及到了对象类实例。让我们剖析一下(这也是我很感兴趣的地方):

..includedictobject.h

147091808228766332.JPEG

这并不是整体架构,但是最有意思的一部分。你或许会想,看起来怪怪的作用符func仅仅是个回调体。每个Python对象都应以一种方式初始化它。

例如,cmpfunc tp_compare;明显表示它与对象之间的比较有些联系。PyIntObject对于比较函数和PyTupleObject的处理将会有所不同。

另外,hashfunc tp_hash;将哈希函数定义为一种类型。例如,string将包含这一功能,但dictionary就没有。猜猜是为什么?

如果你想阅读更多相关知识,可以参阅 Python/C API Reference Manual, "Object Implementation Support", "Type Objects"一节。

接下来将对如下图所示函数在PyInt_Type,PyDict_Type和PyTuple_Type对象中的运行进行比较:

147091808239866464.PNG

由于前三个函数拥有返回值,这提示我们,它们应用在Python抽象对象层。

抽象对象层定义了若干Python对象在运行和分类是应遵守的协议。协议指的是一类用于规定函数功能及其行为的集合。比方说一种函数当且仅当其被用作一组特殊功能(长度,尺寸,连接函数等)时被归为基于序列的类型。

现存在许多协议,我们主要看以下这些:

数字协议:全部数值类型(包括整形、浮点型、复数类型等)

序列化协议:序列化类型(包括字符串、列表、元组等)

映射协议:映射类型(字典)

回到我们的例子。

PyInt_Type

147091808283262173.PNG

PyInt_Type执行数字协议,这也就是tp_as_sequence和tp_as_mapping函数为空的原因了。

所有的静态类型拥有专有的哈希函数,PyInt_Type也是如此(int_hash)。

PyDict_Type

147091808302898104.PNG

Python中的字典比较微妙,虽然它是映射协议的唯一代表,但也会表现出一些序列化协议的性质(实际上函数__contains__就是一类字典键值的排序)。

由于字典类型可变,因此它没有哈希函数(仅PyObject_HashNotImplemented除外)。

PyInt_Type

147091808348323951.PNG

元组类型也相当微妙,虽然元组基于序列化类型,但它常常遵守两种协议:序列化协议和映射协议。这也是函数tp_as_sequence和tp_as_mapping函数非空的原因了。

元组为静态对象,因此拥有哈希函数(tuplehash)。

就此,我希望你充分体会到了CPython的乐趣。虽然只是一个概述(不太深入),但对于深入了解Python内部机制还是很有帮助的。

参考

Python Standard Library

Python/C API Reference Manual

英文原文:http://www.gahcep.com/python-internals-pyobject/

译者:python_baby返回搜狐,查看更多

责任编辑:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值