python之import机制详解

本文详述了Python的import机制,对于理解Python的运行机制很有帮助!

1.标准import:

Python中所有加载到内存的模块都放在 sys.modules 。当 import 一个模块时首先会在这个列表中查找是否已经加载了此模块,如果加载了则只是将模块的名字加入到正在调用 import 的模块的 Local 名字空间中。如果没有加载则从 sys.path 目录中按照模块名称查找模块文件,模块可以是py、pyc、pyd,找到后将模块载入内存,并加到 sys.modules 中,并将名称导入到当前的 Local 名字空间。

一个模块不会重复载入。多个不同的模块都可以用 import 引入同一个模块到自己的 Local 名字空间,其实背后的 PyModuleObject 对象只有一个。这里说一个容易忽略的问题:import 只能导入模块,不能导入模块中的对象(类、函数、变量等)。例如:模块 A(A.py)中有个函数 getName,另一个模块不能通过 import A.getName 将 getName导入到本模块,只能用 from A import getName。

2.嵌套import:

1)顺序嵌套

例如:本模块导入 A 模块(import A),A 中又 import B,B 模块又可以 import 其他模块……
这中嵌套比较容易理解,需要注意的一点就是各个模块的 Local 名字空间是独立的。对于上面的例子,本模块 import A 之后本模块只能访问模块 A,不能访问模块 B 及其他模块。虽然模块 B 已经加载到内存了,如果访问还要再明确的在本模块中 import B。

2)循环嵌套

例如:

文件[A.py]

from B import D
class C:pass

文件[ B.py ]

from A import C
class D:pass

为什么执行 A 的时候不能加载 D 呢?
如果将 A.py 改为:import B 就可以了。
这是怎么回事呢?

RobertChen:这跟Python内部 import 的机制是有关的,具体到 from B import D,Python 内部会分成几个步骤:
(1)在 sys.modules 中查找符号 “B”
(2)如果符号 B 存在,则获得符号 B 对应的 module 对象。
  从 <modult B> 的 __dict__ 中获得符号 “D” 对应的对象,如果 “D” 不存在,则抛出异常。
(3)如果符号 B 不存在,则创建一个新的 module 对象 <module B>,注意,此时,module 对象的 __dict__ 为空。
执行 B.py 中的表达式,填充 <module B> 的 __dict__。
从  <module B> 的 __dict__ 中获得 “D” 对应的对象,如果 “D” 不存在,则抛出异常。

所以这个例子的执行顺序如下:

1、执行 A.py 中的 from B import D 由于是执行的 python A.py,所以在 sys.modules 中并没有 <module B> 存在, 首先为 B.py 创建一个 module 对象 (<module B>) , 注意,这时创建的这个 module 对象是空的,里边啥也没有, 在 Python 内部创建了这个 module 对象之后,就会解析执行 B.py,其目的是填充 <module B> 这个 __dict__。
2、执行 B.py中的from A import C 在执行B.py的过程中,会碰到这一句, 首先检查sys.modules这个module缓存中是否已经存在<module A>了, 由于这时缓存还没有缓存<module A>, 所以类似的,Python内部会为A.py创建一个module对象(<module A>), 然后,同样地,执行A.py中的语句
3、再次执行A.py中的from B import D 这时,由于在第1步时,创建的<module B>对象已经缓存在了sys.modules中, 所以直接就得到了<module B>, 但是,注意,从整个过程来看,我们知道,这时<module B>还是一个空的对象,里面啥也没有, 所以从这个module中获得符号"D"的操作就会抛出异常。 如果这里只是import B,由于"B"这个符号在sys.modules中已经存在,所以是不会抛出异常的。

ZQ:图解如下:

3. 包 import

只要一个文件夹下面有个 __init__.py 文件,那么这个文件夹就可以看做是一个包。包导入的过程和模块的基本一致,只是导入包的时候会执行此包目录下的 __init__.py 而不是模块里面的语句了。另外,如果只是单纯的导入包,而包的 __init__.py 中又没有明确的其他初始化操作,那么此包下面的模块是不会自动导入的。
 例如:
  有下面的包结构:
  PA
  |---- __init__.py
  |---- wave.py
  |---- PB1
        |---- __init__.py
        |---- pb1_m.py
  |---- PB2
        |---- __init__.py
        |---- pb2_m.py
有如下程序:

import sys
import PA.wave              #1
import PA.PB1                #2
import PA.PB1.pb1_m as m1    #3
import PA.PB2.pb2_m          #4
PA.wave.getName()           #5
m1.getName()                #6
PA.PB.pb2_m.getName()       #7

1) 当执行 #1 后,sys.modules 会同时存在 PA、PA.wave 两个模块,此时可以调用 PA.wave 的任何类或函数了。但不能调用 PA.PB1(2) 下的任何模块。当前 Local 中有了 PA 名字。

2) 当执行 #2 后,只是将 PA.PB1 载入内存,sys.modules 中会有 PA、 PA.wave、PA.PB1 三个模块,但是 PA.PB1 下的任何模块都没有自动载入内存,此时如果直接执行 PA.PB1.pb1_m.getName() 则会出错,因为 PA.PB1 中并没有 pb1_m 。当前 Local 中还是只有 PA 名字,并没有 PA.PB1 名 字。

3) 当执行 #3 后,会将 PA.PB1 下的 pb1_m 载入内存,sys.modules 中会有 PA、PA.wave、PA.PB1、PA.PB1.pb1_m 四个模块,此时可以执行 PA.PB1.pb1_m.getName() 了。由于使用了 as,当前 Local中除了 PA 名字,另外添加了 m1 作为 PA.PB1.pb1_m 的别名。

4) 当执行 #4 后,会将 PA.PB2、PA.PB2.pb2_m 载入内存,sys.modules 中会有 PA、PA.wave、PA.PB1、PA.PB1.pb1_m、PA.PB2、PA.PB2.pb2_m 六个模块。当前 Local 中还是只有 PA、m1。
下面的 #5,#6,#7 都是可以正确运行的。

注意的是:如果 PA.PB2.pb2_m 想导入 PA.PB1.pb1_m、PA.wave 是可以直接成功的。最好是采用明确的导入路径,对于 ./.. 相对导入路径还是不推荐用。

(0)

相关推荐

  • python中from module import * 的一个坑

    但还有另外一个问题 - 你以为你修改了某个变量,其实,被from module import *后的那个并没有被更新,非常危险,因为程序有可能还可以正常运行, 只不过结果错了,到了production才被发现就比较惨了. 举个例子: 你定义了一些变量在base模块中: # reference data type class Demo: def __init__(self, name): self.name = name demo = Demo('Demo') # primitive type f

  • 从零学python系列之新版本导入httplib模块报ImportError解决方案

    之前用Python 2.7版本的httplib做接口测试时,运行代码都是正常的, 最近开始用Python 3.3之后,再去看以前的代码,发现import httplib出现错误:Unresolved import :httplib, 运行代码时也报错:ImportError: No module named 'httplib' 查找各种资料发现原来Python 2.x中的"httplib"模块在Python 3.x中变成了"http.client",就怪之前只了解了

  • Python引用(import)文件夹下的py文件的方法

    Python的import包含文件功能就跟PHP的include类似,但更确切的说应该更像是PHP中的require,因为Python里的import只要目标不存在就报错程序无法往下执行.要包含目录里的文件,PHP中只需要给对路径就OK.Python中则不同,下面来看看这个例子. 目录结构: a.py 要 import dir目录下的 b.py 文件.a.py代码如下: 复制代码 代码如下: # coding=utf-8 "import dir 目录下的 b.py 文件"   impo

  • python中import reload __import__的区别详解

    import 作用:导入/引入一个python标准模块,其中包括.py文件.带有__init__.py文件的目录(自定义模块). import module_name[,module1,...] from module import *|child[,child1,...] 注意:多次重复使用import语句时,不会重新加载被指定的模块,只是把对该模块的内存地址给引用到本地变量环境. 实例: pythontab.py #!/usr/bin/env python #encoding: utf-8

  • python中reload(module)的用法示例详解

    前言 本文主要给大家介绍了关于python中reload(module)用法的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 1.Python2中可以和Python3中关于reload()用法的区别. Python2 中可以直接使用reload(module)重载模块. Pyhton3中需要使用如下方式: (1) >>> from imp >>> imp.reload(module) (2) >>> from imp imp

  • Python import用法以及与from...import的区别

    在python用import或者from...import来导入相应的模块.模块其实就是一些函数和类的集合文件,它能实现一些相应的功能,当我们需要使用这些功能的时候,直接把相应的模块导入到我们的程序中,我们就可以使用了.这类似于C语言中的include头文件,Python中我们用import导入我们需要的模块. eg: 复制代码 代码如下: import sys print('================Python import mode==========================

  • Python中import导入上一级目录模块及循环import问题的解决

    import上一级目录的模块 python中,import module会去sys.path搜索,sys.path是个列表,并且我们可以动态修改. 要import某个目录的module,我们sys.path.insert(0,somedir)来加入搜索路径,就可以import了. 既然这样,要import上一级目录的module,可以sys.path.insert(0,parentdir). 不过这种写绝对路径的方式,如果文件放到其它地方,就不行了. 所以用动态方法来获取上一级目录. impor

  • python在不同层级目录import模块的方法

    使用python进行程序编写时,经常会使用第三方模块包.这种包我们可以通过python setup install 进行安装后,通过import XXX或from XXX import yyy 进行导入.不过如果是自己遍写的依赖包,又不想安装到python的相应目录,可以放到本目录里进行import进行调用:为了更清晰的理清程序之间的关系,例如我们会把这种包放到lib目录再调用.本篇就针对常见的模块调用方法汇总下. 一.同级目录下的调有 程序结构如下: -- src     |-- mod1.p

  • python之import机制详解

    本文详述了Python的import机制,对于理解Python的运行机制很有帮助! 1.标准import: Python中所有加载到内存的模块都放在 sys.modules .当 import 一个模块时首先会在这个列表中查找是否已经加载了此模块,如果加载了则只是将模块的名字加入到正在调用 import 的模块的 Local 名字空间中.如果没有加载则从 sys.path 目录中按照模块名称查找模块文件,模块可以是py.pyc.pyd,找到后将模块载入内存,并加到 sys.modules 中,并

  • Python中import机制详解

    Python语言中import的使用很简单,直接使用 import module_name 语句导入即可.这里我主要写一下"import"的本质. Python官方 定义:Python code in one module gains access to the code in another module by the process of importing it. 1.定义: 模块(module):用来从逻辑(实现一个功能)上组织Python代码(变量.函数.类),本质就是*.p

  • python的广播机制详解

    目录 为什么会有广播机制 在矩阵或向量相关运算中的广播机制 1.一般的运算 2.一个矩阵一个向量的情况 3.两个向量 4.矩阵乘法的广播机制 总结 为什么会有广播机制 python语言在设计的时候,就就考虑到用于两个运算的矩阵或向量维度不匹配的问题.例如,我们有矩阵A,让矩阵每个元素都加1,直接使用A+1,就可以完成目的.这其中就用到了python的广播机制,所以在很多python的第三方库中,都支持广播机制,例如Numpy.pytorch. 在矩阵或向量相关运算中的广播机制 1.一般的运算 假

  • Python retrying 重试机制详解

    目录 循环加判断 retrying 总结 我们在程序开发中,经常会需要请求一些外部的接口资源,而且我们不能保证每次请求一定会成功,所以这些涉及到网络请求的代码片段就需要加上重试机制.下面来说一下Python中的重试方法. 循环加判断 最简单的重试方式就是在需要进行重试的代码片段上加一个循环,程序内捕获异常,如果执行成功就退出循环,执行失败就就重复执行相关代码,例如: import requests def req_with_retry(url): retry_max = 10 # 最大重试次数为

  • python字符串Intern机制详解

    字符串在 Python 中是最简单也是最常用的数据类型之一,在 CPython 中字符串的实现原理使用了一种叫做 Intern(字符串驻留)的技术来提高字符串效率.究竟什么是 intern 机制,这种机制又是通过什么方式来提高字符串效率的呢?希望这篇文章能够抛砖引玉. 先来看一段代码: >>> s3 = "hello!" >>> s4 = "hello!" >>> s3 is s4 False >>&

  • Python 垃圾回收机制详解

    目录 1. 引用计数 2. 标记-清除 3. 分代回收 4. 其他 4.1 JNI(Java Native Interface) 总结 Python 的GC模块主要运用了引用计数来跟踪和回收垃圾:通过"标记-清除"解决容器对象可能产生的循环引用问题:通过分代回收以空间换时间进一步提高垃圾回收的效率. 也即采用"引用计数"为主(实时性,一旦没有引用,内存就直接释放了),"标记-清除"与"分代收集"两种机制为辅的策略.      

  • 详解Python中import机制

    Python语言中import的使用很简单,直接使用import module_name语句导入即可.这里我主要写一下"import"的本质. Python官方定义: Python code in one module gains access to the code in another module by the process of importing it. 1.定义: 模块(module):用来从逻辑(实现一个功能)上组织Python代码(变量.函数.类),本质就是*.py文

  • python实现差分隐私Laplace机制详解

    Laplace分布定义: 下面先给出Laplace分布实现代码: import matplotlib.pyplot as plt import numpy as np def laplace_function(x,beta): result = (1/(2*beta)) * np.e**(-1*(np.abs(x)/beta)) return result #在-5到5之间等间隔的取10000个数 x = np.linspace(-5,5,10000) y1 = [laplace_functio

  • Python并发编程线程消息通信机制详解

    目录 1 Event事件 2 Condition 3 Queue队列 4 总结一下 前面我已经向大家介绍了,如何使用创建线程,启动线程.相信大家都会有这样一个想法,线程无非就是创建一下,然后再start()下,实在是太简单了. 可是要知道,在真实的项目中,实际场景可要我们举的例子要复杂的多得多,不同线程的执行可能是有顺序的,或者说他们的执行是有条件的,是要受控制的.如果仅仅依靠前面学的那点浅薄的知识,是远远不够的. 那今天,我们就来探讨一下如何控制线程的触发执行. 要实现对多个线程进行控制,其实

  • python的内存管理和垃圾回收机制详解

    简单来说python的内存管理机制有三种 1)引用计数 2)垃圾回收 3)内存池 接下来我们来详细讲解这三种管理机制 1,引用计数: 引用计数是一种非常高效的内存管理手段,当一个pyhton对象被引用时其引用计数增加1,当其不再被引用时引用计数减1,当引用计数等于0的时候,对象就被删除了. 2,垃圾回收(这是一个很重要知识点): ①  引用计数 引用计数也是一种垃圾回收机制,而且是一种最直观,最简单的垃圾回收技术. 在Python中每一个对象的核心就是一个结构体PyObject,它的内部有一个引

随机推荐