《深度剖析CPython解释器》20. Python类机制的深度解析(第四部分): 实例对象的创建、以及属性访问

摘要:
因为当Python虚拟机执行时,是实例对象在内存中掀起波澜,而类对象只是幕后英雄。因此,在执行之后,模块的本地空间将如下:_在FUNCTION中,Python也将执行相应类型的tp _ Call操作。注意,在创建classGirl对象时,Python虚拟机调用PyType_Ready已初始化classGirl。其中一个动作是继承基类,所以Girl。tp_new实际上是一个对象tp_new,而在PyBaseObject_in-Type中,这个操作被定义为object_new创建类对象。Python虚拟机使用tp_New,创建一个实例对象,并将该对象用于Python虚拟机_New

楔子

介绍完类对象之后,我们来介绍实例对象。我们之前费了老鼻子劲将类对象剖析了一遍,但这仅仅是万里长征的第一步。因为Python虚拟机执行时,在内存中兴风作浪的是一个个的实例对象,而类对象只是幕后英雄。

通过class类对象创建实例对象

我们还以之前的代码为例:

class Girl:

    name = "夏色祭"
    def __init__(self):
        print("__init__")

    def f(self):
        print("f")

    def g(self, name):
        self.name = name
        print(self.name)


girl = Girl()

看一下它的字节码,这里我们只看创建实例对象模的字节码,也就是模块的字节码。

  1           0 LOAD_BUILD_CLASS
              2 LOAD_CONST               0 (<code object Girl at 0x000002B7A85FABE0, file "instance", line 1>)
              4 LOAD_CONST               1 ('Girl')
              6 MAKE_FUNCTION            0
              8 LOAD_CONST               1 ('Girl')
             10 CALL_FUNCTION            2
             12 STORE_NAME               0 (Girl)

 15          14 LOAD_NAME                0 (Girl)
             16 CALL_FUNCTION            0
             18 STORE_NAME               1 (girl)
             20 LOAD_CONST               2 (None)
             22 RETURN_VALUE

我们看到在类构建完毕之后,14 LOAD_NAME这条指令便将刚刚构建的类Girl取了出来、压入运行时栈,然后通过CALL_FUNCTION将栈里面的类弹出、进行调用,得到实例对象,再将实例对象设置在栈顶。18 STORE_NAME将栈顶的实例对象弹出,让符号girl与之绑定,放在local空间中。

所以我们看到调用类对象的指令居然也是CALL_FUNCTION,因为一开始我们说了,类和函数一样,都是要先将PyCodeObject变成PyFunctionObject。

因此执行完毕之后,模块的local空间就会变成这样:

《深度剖析CPython解释器》20. Python类机制的深度解析(第四部分): 实例对象的创建、以及属性访问第1张

在CALL_FUNCTION中,Python同样会执行对应类型的tp_call操作。所以创建实例的时候,显然执行PyType_Type的tp_call,因此最终是在PyType_Type.tp_call中调用Girl.tp_new来创建instance对象的。

需要注意的是,在创建class Girl这个对象时,Python虚拟机调用PyType_Ready对class Girl进行了初始化,其中一项动作就是继承基类,所以Girl.tp_new实际上就是object.tp_new,而在PyBaseObject_Type中,这个操作被定义为object_new。创建class对象和创建instance对象的不同之处正是在于tp_new不同。创建class对象,Python虚拟机使用的是tp_new,创建instance对象,Python虚拟机则使用object_new。使用类重写__new__的话,应该很容易明白。

因此,由于我们创建的不是class对象,而是instance对象,type_call会尝试进行初始化的动作。

static PyObject *
type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    //......
    type = Py_TYPE(obj);
    if (type->tp_init != NULL) {
        int res = type->tp_init(obj, args, kwds);
        if (res < 0) {
            assert(PyErr_Occurred());
            Py_DECREF(obj);
            obj = NULL;
        }
        else {
            assert(!PyErr_Occurred());
        }
    }
    return obj;
}

那么这个tp_init是哪里来的的,是在使用tp_new创建类对象的时候来的,tp_init在PyType_Ready时会继承PyBaseObject_Type的object_init操作。但正如我们之前说的那样,因为A中的定义重写了__init__,所以在 fixup_slot_dispatchers 中,tp_init会指向slotdef中指定的与__init__对应的slot_tp_init。并且还会设置tp_alloc,这与内存分配有关,源码中会有所体现。

static PyObject *
type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
{
    //........
	
    //tp_alloc被设置为PyType_GenericAlloc, 表示为实例对象分配内存, 因为内存大小的元信息存在对应的类对象中
    //并且在分配内存的同时会将实例对象的ob_type设置为对应的类对象
    type->tp_alloc = PyType_GenericAlloc;
    if (type->tp_flags & Py_TPFLAGS_HAVE_GC) {
        type->tp_free = PyObject_GC_Del;
        type->tp_traverse = subtype_traverse;
        type->tp_clear = subtype_clear;
    }
    else
        type->tp_free = PyObject_Del;
	
    //设置tp_init
    fixup_slot_dispatchers(type);
    //......
}


PyObject *
PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)
{
    PyObject *obj;
    const size_t size = _PyObject_VAR_SIZE(type, nitems+1);
    
    //分配内存
    if (PyType_IS_GC(type))
        obj = _PyObject_GC_Malloc(size);
    else
        obj = (PyObject *)PyObject_MALLOC(size);

    if (obj == NULL)
        return PyErr_NoMemory();

    memset(obj, '

免责声明:内容来源于网络,仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇linux上kafka搭建小结Android通过反射获取资源ID下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

guxh的python笔记三:装饰器

1,函数作用域 这种情况可以顺利执行: total = 0 def run(): print(total) 这种情况会报错: total = 0 def run(): print(total) total = 1 这种情况也会报错: total = 0 def run(): total += 1...

第一篇 pycharm安装及设置

1.下载pycharm包安装 (1)安装好Python 安装后在命令行中输入python 检查是否安装成功(进入py的交互行) (2)安装好Python 需要两个地方配置到环境变量里面 1.python的安装目录(python.exe在该文件夹中) 2.以及安装目录中的scripts(模块) 2.破解pycharm 3.pycharm的设置 (1)设置通关...

Detectron的安装以及解决方案

转载:https://blog.csdn.net/comway_Li/article/details/85163607 本博客介绍了如何安装Detectron,其依赖项(包括Caffe2)和COCO数据集。 安装前所需知道的知识: a、Detectron运营商目前没有CPU实施;需要GPU系统。 b、caffe2 已经集成到pytorch1.0中,所以框架...

CentOS搭建python开发环境

装了个CentOS 5.5,想在上面搭个python的开发环境,可是还是遇到了很多问题,记录一下过程: 1、python升级 查看python版本 python -V Python 2.4.3 因为python3的变化很大,还是希望用新的版本,goole了一把,看到有一个指导贴: cd /usr/local/src wget http://www.py...

python多线程下载网页图片并保存至特定目录

#!python3 #multidownloadXkcd.py - Download XKCD comics using multiple threads. import requests import bs4 import os import threading # os.mkdir('xkcd', exist_ok=True) # sto...

python自动控制windows、Android的软件用来实现机器人流程自动化--RPA,外挂、自动化测试等方面的解决方案

机器人流程自动化--RPA,外挂、自动化测试等方面要求的用脚本自动化操作各平台的软件,说白了就是基于图像识别和控件识别,模拟人工输入和点击操作来实现的,从而到达代替手动操作,降低劳动强度。一、windows系统:   方案一、组合以下库和软件来达到目的:      1、pyautogui库:模拟按键...