python的导入系统import module模块

模块内部封装了很多实用的功能,有时在模块外部调用就需要将其导入,导入模块简单划分,
实际上就只有两种:
import ……

from …… import

参考:https://docs.python.org/zh-cn/3/reference/import.html

1,导入模块的的几种方式

模块是什么?
模块实际上就是 以.py为结尾的文件
注意点:自定义的模块尽量不要和系统模块重名

模块内部封装了很多实用的功能,有时在模块外部调用就需要将其导入,导入模块简单划分,实际上就只有两种:

import ……
from …… import

详细一点划分有五种:

1,improt 模块名
调用:模块名.功能名
2,import 模块名 as 别名
调用:别名.功能名
3,from 模块名 import 功能名
调用:直接功能名
4,from 模块名 import 功能名 as 别名
调用: 直接拿别名来用
5,from 模块名 import * (用 * 号 一次性导入所有功能)
调用:直接功能名
注意点:* 号没法用别名

2,模块的搜索路径

sys.path 返回导入模块时的搜索路径集,是一个list列表。

从上面列出的目录里依次查找要导入的模块文件
’ ’ 表示当前路径
列表中的路径的先后顺序代表了python解释器在搜索模块时的先后顺序

可以添加新的模块:

sys.path.append(‘/home/python/xxx’)
sys.path.insert(0, ‘/home/python/xxx’) # 可以确保先搜索这个路径
注意点:sys.path.append(path)和sys.path.insert(path)添加的相关路径,在退出交互式环境或者IDE后会自动消失。

In [37]: sys.path.insert(0,”/home/python/xxxx”)
In [38]: sys.path
Out[38]:
[‘/home/python/xxxx’,
”,
‘/usr/bin’,
‘/usr/lib/python35.zip’,
‘/usr/lib/python3.5’,
‘/usr/lib/python3.5/plat-x86_64-linux-gnu’,
‘/usr/lib/python3.5/lib-dynload’,
‘/usr/local/lib/python3.5/dist-packages’,
‘/usr/lib/python3/dist-packages’,
‘/usr/lib/python3/dist-packages/IPython/extensions’,
‘/home/python/.ipython’]1234567891011121314

3,重新导入模块

模块被导入后,import module不能重新导入模块,重新导入需用reload

创建一个reload_test.py文件,里面写一个test方法

打开 ipython 终端导入 reload_test 模块

修改reload_test.py 文件中的test方法

再一次import reload_test 模块 然后调用test方法会发现值没变

原因:import 导入模块只会导入一次,因此即使模块被修改了,import也不会重新导入
解决方案

关闭终端,重新打开,然后再import 导入一次
用 reload 可以在不关闭终端的情况下重新导入

4,多模块开发时,要注意修改导入模块的值时,from……import 与 import 导入模块的区别

main.py

from send import *
from show import *

主流程

def main():
# 发女朋友
send()
# 秀恩爱
show()

if name == ‘main‘:
main()12345678910111213

send.py

import girl

from girl import *

发女朋友

def send():
print(“发女朋友了,翠花是你的了”)
#girl.have_girl = True
have_girl = True12345678

show.py

import girl

from girl import *

秀恩爱

def show():
if have_girl == True:
#if girl.have_girl == True:
print(“我有女朋友了,好开心”)
else:
print(“单身贵族”)12345678910

girl.py

共同变量模块

have_girl = False #False代表没有女朋友 True代表有女朋友123

运行结果:

发女朋友了,翠花是你的了
单身贵族
12

是不是有点惊讶?结果难道,不应该是这样的吗:

发女朋友了,翠花是你的了
我有女朋友了,好开心
12

会产生这种情况的原因:
import girl 可以理解为地址的复制,也就是 引用 用来修改值

from girl import * 可以理解成内容的复制,也就是深拷贝,那么深拷贝有什么特点,深拷贝最大的特点就是数据独立。


循环导入的最好的解决方法是从架构上优化,即调整模块和模块成员变量的设计。一个好的原则是:可导出的成员变量,都不应该依赖于导入进来的成员变量。
但是在业务开发的过程中,总会遇到通过架构层面解决不了的导入问题,这时候就只能通过语言层面来解决了。

目录结构(下面的案例的目录结构都是这样的):

root.py
/pack1
    __init__.py
    module_a.py
/pack2
    __init__.py
    module_b.py
module_c.py
module_d.py

循环导入例子

首先看一下什么是循环导入和循环导入的原因。
root.py

from pack1.module_a import class_a

module_a.py

print "start init module a"
from pack2.module_b import class_b
class class_a():
    def f(self):
        class_b
print "init module a"

module_b.py

print "start init module b"
from pack1.module_a import class_a
class class_b():
    def f(self):
        class_a
print "init module b"

会报错:

start init module a
start init module b
Traceback (most recent call last):
  File "E:/my_demo/demo2016/bѭ������/s2/root.py", line 2, in <module>
    from pack1.module_a import class_a
  File "E:\my_demo\demo2016\bѭ������\s2\pack1\module_a.py", line 2, in <module>
    from pack2.module_b import class_b
  File "E:\my_demo\demo2016\bѭ������\s2\pack2\module_b.py", line 2, in <module>
    from pack1.module_a import class_a
ImportError: cannot import name class_a

代码执行的流程:

  1. 执行root.py的from pack1.module_a import class_a,发现需要导入模块module_a
  2. 一个空的字典会被创建,对应module_a的globals
  3. module_a的代码会被执行,当执行到from pack2.module_b import class_b时,发现需要导入模块module_b
  4. 一个空的字典会被创建,对应module_b的globals
  5. module_b的代码会被执行,当执行到from pack1.module_a import class_a时,发现需要导入模块module_a,但是此时已经有module_a的globals了,所以直接访问字典里的class_a,但是由于module_a的globals还是空的,即里面没有class_a,所以抛出异常

参考文档
所以根本原因是:在导入的时候,module_b需要访问module_a的变量class_a,但是class_a没有初始化完成
所以解决方法有两个:

  1. 在导入的时候,让module_b不要访问module_a的变量,也就是方案一
  2. class_a初始化完成后,才让module_b访问module_a的变量,也就是方案二和三

方案一、使用import ...代替 from...import...

root.py

import pack1.module_a

module_a.py

print "start init module a"
import pack2.module_b 
class class_a():
    def f(self):
        m_b.class_b
print "init module a"
if __name__ == '__main__':
    pass

module_b.py

print "start init module b"
import pack1.module_a 
class class_b():
    def f(self):
        pack1.module_a.class_a
print "init module b"

module_a和module_b都会被编译,终端会输出:

start init module a
start init module b
init module b
init module a

即首先编译a,编译过程中发现需要编译b,编译b完成后,编译a剩下的部分、

这个案例不使用from....import....,而使用import,这样是可以成功循环导入的,不过一个缺点是,每次访问module的时候,都需要写全路径,例如pack1.module_a.class_a,非常繁琐。
一个优化的方案是导入的时候,使用import....as... 例如:import pack1.module_a as m_a。但是很奇怪的是,在module_a中可以这样用,但是在module_b中不可以,否则就会导致报错。还有如果把roo.py改为import pack2.module_b,就会反过来,即module_b中可以这样用,但是在module_a中不可以。所以准确点应该是在root.py导入的模块中可以使用,但是在其他模块不能使用。所以import....as...这个方案并不好。
注意,import...只能import到模块,不能import模块里面的成员变量,例如import pack1.module_a.class_a 是不可以的

这个方案的缺点就是访问模块里面的成员变量太繁琐

方案二、把导入放在后面

root.py

from pack1.module_a import class_a

module_a.py

print "start init module a"
#from pack2.module_b import class_b #放在这里会报错
class class_a():
    def f(self):
        # m_b.class_b
        pass

from pack2.module_b import class_b #放在这里不会
class class_c():
    def f(self):
        class_b
print "init module a"

module_b.py

print "start init module b"
from pack1.module_a import class_a
class class_b():
    def f(self):
        class_a
print "init module b"

当存在类似的依赖关系:class_c依赖class_b依赖class_a,然后class_a和class_c在同一个模块时,可以使用这种方案。
from pack2.module_b import class_b这句放在class_a后面,这样在module_b中访问module_a.class_a是成功的,因为class_a的定义代码已经执行完成,并被添加到module_a的globals中。

方案三、把导入语句放在语句块中

root.py

from pack1.module_a import func_a

print 'root start run func a'
func_a()
print 'root end run func a'

module_a.py

print "start init module a"

def func_a():
    from pack2.module_b import func_b
    func_b()
    print 'run func a'
print "init module a"

module_b.py

print "start init module b"

def func_b():
    from pack1.module_a import func_a
    print 'run func b'

print "init module b"

输出:

start init module a
init module a
root start run func a
start init module b
init module b
run func b
run func a
root end run func a

在需要使用func_b的时候,才进行导入操作,这样在执行module_b的时候,module_a已经初始化完成,module_a的globals已经有func_a了,所以导入不会报错。

查看已经导入的module情况

import sys
from pack1.module_a import func_a
print sys.modules  
# {'pack1': <module 'pack1' from 'E:\my_demo\demo2016\bѭ������\s4\pack1\__init__.pyc'>,}
print sys.modules['pack1.module_a'].__dict__
# {'func_a': <function func_a at 0x0254FB30>, '__doc__': None}
sys.modules['pack1.module_a'].func_a_tmp=sys.modules['pack1.module_a'].func_a

通过sys.modules可以访问所有当前已导入的模块。
modules是一个字典,key是模块的路径,例如pack1.module_a,value是一个模块对象
模块对象中,属性名是模块中全局变量的名字,即sys.modules['pack1.module_a'].__dict__等于module_a里面的globals()

所以,当在module_b中执行from pack1.module_a import class_a时,相当于执行代码:

import sys
if 'pack1.module_a' in sys.modules:
    if hasattr(sys.modules['pack1.module_a'],"class_a"):
        sys.modules['pack2.module_b'].class_a=sys.modules['pack1.module_a'].class_a
    else:
        raise Exception(u"循环导入异常")
else:
    #执行导入pack1.module_a的操作,也就是初始化一个module对象,然后令sys.modules['pack1.module_a']=这个对象

所以解决循环导入的问题,就相当于使上面的代码不会执行到raise Exception(u"循环导入异常")这一句,方案一和方案二都是通过这种方法解决的。

作者:

喜欢围棋和编程。

 
发布于 分类 编程标签

发表评论

邮箱地址不会被公开。