分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 代码编程

Python模块动态加载机制

发布时间:2023-09-06 01:06责任编辑:郭大石关键词:暂无标签

本文和大家分享的主要是python中模块动态加载机制相关内容,一起来看看吧,希望对大家学习python有所帮助。

import指令

来看看import sys所产生的指令:

co_consts : (0, None)

co_names : (’sys’,)

0 LOAD_CONST 0 (0)

2 LOAD_CONST 1 (None)

4 IMPORT_NAME 0 (sys)

6 STORE_NAME 0 (sys)

可以看到import的结果是赋值给变量sys并存储在当前frame的local名字空间中,使得后续使用sys.path就能很快找到这个符号了.具体看看IMPORT_NAME指令做了什么动作:

TARGET(IMPORT_NAME) {

PyObject *name = GETITEM(names, oparg); //获得’sys’ PyUnicodeObject

PyObject *fromlist = POP(); // None

PyObject *level = TOP(); // 0

PyObject *res;

res = import_name(f, name, fromlist, level);

Py_DECREF(level);

Py_DECREF(fromlist);

SET_TOP(res);

if (res == NULL)

goto error;

DISPATCH();

}

这部分是收集将要import操作的所需信息,然后调用import_name :

[ceval.c]

static PyObject * import_name(PyFrameObject *f, PyObject *name, PyObject *fromlist, PyObject *level)

{

_Py_IDENTIFIER(__import__);

PyObject *import_func, *res;

PyObject* stack[5];

//获得内建函数__import__

import_func = _PyDict_GetItemId(f->f_builtins, &PyId___import__);

/* Fast path for not overloaded __import__. */

if (import_func == PyThreadState_GET()->interp->import_func) {

int ilevel = _PyLong_AsInt(level);

if (ilevel == -1 && PyErr_Occurred()) {

return NULL;

}

res = PyImport_ImportModuleLevelObject(

name,

f->f_globals,

f->f_locals == NULL ? Py_None : f->f_locals,

fromlist,

ilevel);

return res;

}

...

}

传进来的参数列表分别是,当前frame的PyFrameObject对象,表示’sys’的PyUnicodeObject, Py_None对象,表示0的PyLongObject.首先从内建f->builtins中获取__import__函数.此时它已经是一个包装过了的PyCFunctionObject对象了,在上一篇的builtins初始化时对每一个方法进行了包装.

if (import_func == PyThreadState_GET()->interp->import_func)是用来判断__import__是否被程序员重载了,这里不考虑被重载的情况. PyImport_ImportModuleLevelObject函数内比较复杂,因为它还要处理如import xml.sax这样的结构,好像调用时PyImport_ImportModuleLevelObject压根没有用到import_func这个内建import方法,但其实他们是殊途同归的,还记得内建方法的数组builtin_methods吗:

[bltinmodule.c]

static PyMethodDef builtin_methods[] = {

...

{"__import__", (PyCFunction)builtin___import__, METH_VARARGS | METH_KEYWORDS, import_doc},

...

};

static PyObject * builtin___import__(PyObject *self, PyObject *args, PyObject *kwds)

{

static char *kwlist[] = {"name", "globals", "locals", "fromlist",

"level", 0};

PyObject *name, *globals = NULL, *locals = NULL, *fromlist = NULL;

int level = 0;

if (!PyArg_ParseTupleAndKeywords(args, kwds, "U|OOOi:__import__",

kwlist, &name, &globals, &locals, &fromlist, &level))

return NULL;

return PyImport_ImportModuleLevelObject(name, globals, locals,

fromlist, level);

}

最终调用的其实是同一个PyImport_ImportModuleLevelObject函数.

很混乱有没有,好,重新整理下import的实现过程.

import机制

对于任何import操作, python虚拟机都要做到:

对运行时sys.modules全局模块池的维护

解析和搜索module路径

对不同文件的module的动态加载机制

import的形式也有很多种,最简单的形式如import os ,复杂一点就是import x.y.z ,注入from和as与import结合的,也会被分析后变成import x.y.z的形式.所以我们分析import的实现代码就以import x.y.z作为指令动作.

当以import x.y.z形式时,调用的参数分别是:

res = PyImport_ImportModuleLevelObject(

name, //表示’x.y.z’的PyUnicodeObject

f->f_globals, // frame的global名字空间

f->f_locals == NULL ? Py_None : f->f_locals,// frame的local名字空间

fromlist, // None值

ilevel); // 0

深入这个函数来看:

PyObject * PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,

PyObject *locals, PyObject *fromlist,

int level)

{

_Py_IDENTIFIER(_find_and_load);

_Py_IDENTIFIER(_handle_fromlist);

PyObject *abs_name = NULL;

PyObject *final_mod = NULL;

PyObject *mod = NULL;

PyObject *package = NULL;

PyInterpreterState *interp = PyThreadState_GET()->interp;

int has_from;

abs_name = name;

mod = PyDict_GetItem(interp->modules, abs_name);

if (mod != NULL && mod != Py_None) { //如果全局modules里已经有了,说明重复引入模块

...

}

}

else { //该模块第一次引入

mod = _PyObject_CallMethodIdObjArgs(interp->importlib,

&PyId__find_and_load, abs_name,

interp->import_func, NULL);

}

//处理from xxx import xxx语句

has_from = 0;

if (fromlist != NULL && fromlist != Py_None) {

has_from = PyObject_IsTrue(fromlist);

if (has_from < 0)

goto error;

}

if (!has_from) { //不是from xxx形式的

Py_ssize_t len = PyUnicode_GET_LENGTH(name);

if (level == 0 || len > 0) {

Py_ssize_t dot;

//查找是模块名是否含有.不含返回-1,含会返回其索引

dot = PyUnicode_FindChar(name, ’.’, 0, len, 1);

if (dot == -1) {

/* No dot in module name, simple exit */

final_mod = mod;

Py_INCREF(mod);

goto error;

}

if (level == 0) {

PyObject *front = PyUnicode_Substring(name, 0, dot);

if (front == NULL) {

goto error;

}

final_mod = PyImport_ImportModuleLevelObject(front, NULL, NULL, NULL, 0);

Py_DECREF(front);

}

else {

...

}

}

else {

final_mod = mod;

Py_INCREF(mod);

}

}

else {

final_mod = _PyObject_CallMethodIdObjArgs(interp->importlib,

&

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved