Python基础 | 1.Python对象初探

文章内容部分引自:陈儒——《Python源码剖析》
https://github.com/python/cpython/blob/master/Include/object.h
https://docs.python.org/3/c-api/typeobj.html

一、简述Python中的对象

在学习Python时,脑子里一定要有清晰的概念,那就是————在Python世界中,一切都是对象

1.在Python世界中,一切皆对象。

  • 对象是Python中最核心的一个概念,在Python的世界中,一切都是对象。
    • 一个整数是一个对象,一个字符串是一个对象,等等;
    • 更为奇妙的是,整数类型是一个对象,字符串类型也是一个对象。
    • 换句话说,面向对象理论中的“类”和“对象”这两个概念在Python中都是通过Python内的对象来实现的。

2.内建类型对象

  • 在Python中,已经预先定义了一些类型对象。
    • 比如int类型、string类型、dict类型等等。
    • 上述这些类型对象实现了面向对象中“类”的概念,这些内建类型对象通过“实例化”,可以创建内建类型对象的实例对象,比如int对象、string对象、dict对象等。
    • 我的理解:
      number = 1000
      
      • 这句代码是对 int类型对象(类) 的实例化,创建了一个 int对象,这个 int对象 存在于申请的内存中,而 变量number 是这个 int对象(值为1000) 的引用。
      • 图片形式展示为:
      • 内建类型对象

3.自定义类型对象

  • Python允许程序员通过 class A(object) 这样的表达式自己定义类型对象。
    • 这样的类型对象,同样可以实例化操作,创建的对象叫作“实例对象”。
    • Python中有着许多千差万别的对象,这些对象之间又存在着各种复杂的关系,从而构成了我们称之为“类型系统”或“对象体系”的东西。

二、Python对象初探

1.对象机制的基石——PyObject

  • 在Python中,一切都是对象,而所有的对象都有相同的东西,这些内容在PyObject中定义,因此PyObject是整个Python对象机制的核心。
  • 我们可以理解为所有的类都是继承于 基类PyObject
    [object.h]
    typedef struct _object {
        _PyObject_HEAD_EXTRA   //详细介绍在下述 a.关于 _PyObject_HEAD_EXTRA
        Py_ssize_t ob_refcnt;   //详细介绍在下述 b.关于 Py_ssize_t ob_refcnt
        PyTypeObject *ob_type;   //详细介绍在下述 c.关于 PyTypeObject *ob_type
    } PyObject;
    
  • 现在我们来分析PyObject:
    • a.关于_PyObject_HEAD_EXTRA
      /* Define pointers to support a doubly-linked list of all live heap objects. */
      #define _PyObject_HEAD_EXTRA            \
          struct _object *_ob_next;           \
          struct _object *_ob_prev;
      
      • 环状双向链表 refchain,在python程序中创建的任何对象都会放在refchain链表中。( _ob_next = refchain中的上一个对象;_ob_prev = refchain中的下一个对象)
      • _PyObject_HEAD_EXTRA这个宏的作用仅仅是在DEBUG模式下,将全部的对象使用双链表链起来方便DEBUG而已。因此暂忽略不考虑。(下划线开头的一般内部使用)
    • b.关于Py_ssize_t ob_refcnt
      • 在Python2.5中,这段是 int ob_refcnt,因此我们认为Py_ssize_tint效果是一样的,那么为什么使用Py_ssize_t呢???
      • Python在后来的版本中创建了 Py_ssize_t 类型用作索引的类型,它是为了容纳64位体系结构。(这也是与int的不同,64位系统的计算机越来越多)
      • 也就是说在64位系统中, 它是64位int整型, 32位系统就是int. _W64,是为了兼容64位系统存在的。 所以 Py_ssize_t 的本质就是 int.
      • 也就可以认为ob_refcnt 是一个 整型变量。用作 引用计数 的计数器。引用计数与Python的垃圾收集机制有关。
    • c.关于PyTypeObject *ob_type
      • ob_type 是一个指向 PyTypeObject 的指针,用来指定一个对象类型的类型对象。
      • 也就是说,表示了对象的类型信息, 诸如int, string, function等。
    • d.因此,Python 的对象机制十分简单,就是 引用计数 + 类型信息。
  • 每一个Python的对象除了必须有PyObject这个内容以外,还要有一些额外的东西,也就是每个对象独特的东西,这样才能使得Python有许多个千差万别的对象。

2.定长对象和变长对象

(1)定长对象与变长对象的直观理解

  • a.定长对象与变长对象的含义
    • 定长对象:这类对象在内存中的占用大小相同。例如:数字1和数字1000在内存中的占用大小是相同的,都是28字节。
    • 变长对象:这类对象在内存中的占用大小不相同。例如:字符串'a'和字符串'ab'和字符串'abc'在内存中的占用全部是不能相同的。
  • b.定长对象和变长对象的例子
    • 定长对象(以int类型为例)
      import sys
      
      a = 1
      b = 1000
      c = 2 ** 30 - 1
      print('the memory size of a is:',sys.getsizeof(a),'byte')
      print('the memory size of b is:',sys.getsizeof(b),'byte')
      print('the memory size of c is:',sys.getsizeof(c),'byte')
      
      # 输出结果为
      the memory size of a is: 28 byte
      the memory size of b is: 28 byte
      the memory size of c is: 28 byte
      
    • 变长对象(以str类型为例)
      import sys
      
      a = 'a'
      ab = 'ab'
      abc = 'abc'
      print('the memory size of a is:',sys.getsizeof(a),'byte')
      print('the memory size of ab is:',sys.getsizeof(ab),'byte')
      print('the memory size of abc is:',sys.getsizeof(abc),'byte')
      
      # 输出结果为
      the memory size of a is: 50 byte
      the memory size of ab is: 51 byte
      the memory size of abc is: 52 byte
      

(2)定长对象和变长对象的结构体

  • a.定长对象的结构体就是 基类PyObject
    • 上面已经详细介绍过,不再介绍
  • b.变长对象的结构体——PyVarObject
    • PyVarObject的结构体
      [object.h]
      typedef struct {
          PyObject ob_base;
          Py_ssize_t ob_size; /* Number of items in variable part */
      } PyVarObject;
      
      • 可以看到,PyVarObject继承了基类PyObject,并添加了 Py_ssize_t ob_size
      • 关于Py_ssize_t上面已经介绍过,不再介绍。简单理解为就是 int
      • 变长对象通常都是容器,ob_size这个成员实际上指明了变长对象中一个容纳了多少个元素。【特别注意:是元素的个数,而不是字节数
  • c.PyVarObject 实际上只是对 PyObject 的一个扩展而已,因此,对于任何一个 PyVarObject,其所占的内存,开始部分的字节的意义和 PyObject 是一样的。换句话说,在 Python 内部,每一个对象都拥有相同的对象头部。这就使得在 Python 中,对对象的引用变得非常的统一,我们只需要用一个 PyObject* 指针就可以引用任意的一个对象。而不论该对象是一个什么对象。

(3)定长对象和变长对象在内存布局上的关系

  • 对象&内存
  • 从图片中反应了上述内容:
    • 在Python中,每一个对象都拥有相同的对象头部(蓝色部分)
    • PyVarObject是对PyObject的扩展,多了ob_size(绿色部分)
    • 定长对象的Other是固定的(某一范围内,如int在每 230内相同),变长对象的Other只要ob_size(即元素个数)不相同,大小就不相同。(灰色部分)

3.类型对象

前面已讲:Python 的对象机制十分简单,就是 引用计数 + 类型信息。

a.类型对象 _typeobject

  • 当在内存中分配空间、创建对象的时候,毫无疑问我们需要知道申请多大的空间,显然这不是一个定值。因为不同的对象需要不同的空间,那么,对象所需的内存空间的大小的信息在哪里呢?
  • 对象所需的内存空间的大小信息,不明显的存在与PyObject的定义中,而是隐身于PyObject中。
  • 实际上,占用内存空间的大小是对象的一种元信息,这样的元信息是与对象所属类型息息相关的,因为它一定会出现在与对象所对应的类型对象中。
  • 类型对象 _typeobject 的源代码:
    [object.h]
    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;
        Py_ssize_t tp_vectorcall_offset;
        getattrfunc tp_getattr;
        setattrfunc tp_setattr;
        PyAsyncMethods *tp_as_async; /* formerly known as tp_compare (Python 2)
                                        or tp_reserved (Python 3) */
        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 */
        unsigned long tp_flags;
    
        const char *tp_doc; /* Documentation string */
    
        /* call function for all accessible objects */
        traverseproc tp_traverse;
    
        /* delete references to contained objects */
        inquiry tp_clear;
    
        /* rich comparisons */
        richcmpfunc tp_richcompare;
    
        /* weak reference enabler */
        Py_ssize_t tp_weaklistoffset;
    
        /* 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;
    
        destructor tp_finalize;
    
    } PyTypeObject;
    
    • 可能你看不懂,我现在的水平也tm看不懂。但是当前阶段可以知道分为哪些组成部分。官网翻译
      • 类型名 tp_name
      • 创建该类型对象时分配内存空间的大小信息 tp_basicsize, tp_itemsize
      • 与该类型对象相关联的操作信息
      • 类型的类型信息

4.类型的类型

仔细观察PyTypeObject,发现在其定义的最开始,有PyObject_VAR_HEAD,这意味着Python中的类型实际上也是一种对象。那么问题来了,类型对象的类型是什么呢?对于其他对象,可以通过与其关联的类型对象确定其类型,那么通过什么来确定一个对象是类型对象呢?————答案:PyType_Type

(1)PyType_Type源码

[typeobject.c]
PyTypeObject PyType_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "type",                                     /* tp_name */
    sizeof(PyHeapTypeObject),                   /* tp_basicsize */
    sizeof(PyMemberDef),                        /* tp_itemsize */
    (destructor)type_dealloc,                   /* tp_dealloc */
    offsetof(PyTypeObject, tp_vectorcall),      /* tp_vectorcall_offset */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_as_async */
    (reprfunc)type_repr,                        /* tp_repr */
    0,                                          /* tp_as_number */
    0,                                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    0,                                          /* tp_hash */
    (ternaryfunc)type_call,                     /* tp_call */
    0,                                          /* tp_str */
    (getattrofunc)type_getattro,                /* tp_getattro */
    (setattrofunc)type_setattro,                /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
    Py_TPFLAGS_BASETYPE | Py_TPFLAGS_TYPE_SUBCLASS |
    Py_TPFLAGS_HAVE_VECTORCALL,                 /* tp_flags */
    type_doc,                                   /* tp_doc */
    (traverseproc)type_traverse,                /* tp_traverse */
    (inquiry)type_clear,                        /* tp_clear */
    0,                                          /* tp_richcompare */
    offsetof(PyTypeObject, tp_weaklist),        /* tp_weaklistoffset */
    0,                                          /* tp_iter */
    0,                                          /* tp_iternext */
    type_methods,                               /* tp_methods */
    type_members,                               /* tp_members */
    type_getsets,                               /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    offsetof(PyTypeObject, tp_dict),            /* tp_dictoffset */
    type_init,                                  /* tp_init */
    0,                                          /* tp_alloc */
    type_new,                                   /* tp_new */
    PyObject_GC_Del,                            /* tp_free */
    (inquiry)type_is_gc,                        /* tp_is_gc */
};

(2)PyTypeObject与PyType_Type的关系

PyType_Type 在 Python 的类型机制中是一个至关重要的对象,所有用户自定义 class 所对应的 PyTypeObject 对象都是通过这个对象创建的。

  • 一般PyTypeObject 与 PyType_Type 的关系
    >>> class Dog(object):
    ...     pass
    ...
    >>> Dog.__class__
    <class 'type'>
    >>> type.__class__
    <class 'type'>
    >>> 
    
    • 上面的输出 <class 'type'> 就是Python内部的 PyType_Type ,它是所有 class 的 class,所以它在Python 中被称为 metaclass。

5.引用计数

见下一部分:Python基础 | 2.变量、内存表现、引用计数

posted on 2020-06-12 20:43  wangxx06  阅读(500)  评论(1编辑  收藏  举报

导航