python模块导入的方式

常规导入

常规导入是最常使用的导入方式,如下

import time
import os

虽然这节省了空间,但是却违背了Python风格指南。Python风格指南建议将每个导入语句单独成行。 有时在导入模块时,你想要重命名这个模块采用如下方式

import time as mytime
import os as myos

相对导入

# demo1
from ..test2.demos2 import *
#如果直接运行demo1 ValueError: attempted relative import beyond top-level package
#demo1是某个包中的一个模块,而你试图以脚本模式执行,这种模式不支持相对导入。
可以采用绝对导入解决
from 根目录名.test.test1.demo1 import  *

python 中只能在package中使用相对导入,不能在用户的应用程序中使用相对导入,因为不论是相对导入还是绝对导入,都是相当于当前模块来说的,对于用户的主应用程序,也就是入口文件,模块名总是为“ main ”, 所以用户的应用程序必须使用绝对导入,而package中的导入可以使用相对导入。
在这里,相对导入 ., .., 应该理解为在: . : 在当前的package中进行查找 .. : 在上一层的package中进行查找

#demo1.py
a=2
b=3

# demos2.py
from ..test1.demo1 import *
c=4
d=5
print(b)

#test
from 根目录名.test.test2.demos2 import *
print(a)

#运行test 输出 3 2 运行demos2.py ValueError: attempted relative import beyond top-level package

使用from语句导入

很多时候你只想要导入一个模块或库中的某个部分

from re import RegexFlag

使用场景中上面的做法是更好的。在复杂的代码库中,能够看出某个函数是从哪里导入的这点很有用的。不过,如果你的代码维护的很好,模块化程度高,那么只从某个模块中导入一部分内容也是非常方便和简洁的。
如果你正好要写自己的模块或包,有人会建议你在__init__.py文件中导入所有内容,让模块或者包使用起来更方便。我个人更喜欢显示地导入,而非隐式地导入.
反斜杠是Python中的续行符,告诉解释器这行代码延续至下一行。

from os import path,name
from os import (path,name)
这是一个有用的技巧,不过你也可以换一种方式:

from os import path,\
    name

#spam.py
print('from the spam.py')

money=1000

def read1():
    print('spam->read1->money',money)

def read2():
    print('spam->read2 calling read')
    read1()

def change():
    global money
    money=0
#测试一:导入的函数read1,执行时仍然回到spam.py中寻找全局变量money
#test.py
from spam import read1
money=1000
read1()
'''
执行结果:
from the spam.py
spam->read1->money 1000
'''

#测试二:导入的函数read2,执行时需要调用read1(),仍然回到spam.py中找read1()
#test.py
from spam import read2
def read1():
    print('==========')
read2()

'''
执行结果:
from the spam.py
spam->read2 calling read
spam->read1->money 1000
'''

#测试三:导入的函数read1,被当前位置定义的read1覆盖掉了
#test.py
from spam import read1
def read1():
    print('==========')
read1()
'''
执行结果:
from the spam.py
==========
'''
from spam import *
#把spam中所有的不是以下划线(_)开头的名字都导入到当前位置,大部分情况下我们的python程序不应该使用这种导入方式,因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差,在交互式环境中导入时没有问题。 
from spam import * #将模块spam中所有的名字都导入到当前名称空间
print(money)
print(read1)
print(read2)
print(change)

'''
执行结果:
from the spam.py
<function read1 at 0x1012e8158>
<function read2 at 0x1012e81e0>
<function change at 0x1012e8268>
'''
__all__来控制*(用来发布新版本)
在spam.py中新增一行

__all__=['money','read1'] #这样在另外一个文件中用from spam import *就这能导入列表中规定的两个名字

循环导入

如果你创建两个模块,二者相互导入对方,那么就会出现循环导入。例如:

a.py

import b

def demo():
    print("in a_demo")
    a.demo()

demo()

在同个文件夹中创建另一个模块,将其命名为b.py。

import a

def demo():
    print('In b_demo"')
    a.demo()

demo()

如果你运行任意一个模块,都会引发AttributeError。这是因为这两个模块都在试图导入对方。简单来说,模块a想要导入模块b,但是因为模块b也在试图导入模块a(这时正在执行),模块a将无法完成模块b的导入。一般来说,解决办法是重构代码,避免发生这种情况。

init.py

只要是第一次导入包或者是包的任何其他部分,都会依次执行包下的__init__.py文件(我们可以在每个包的文件内都打印一行内容来验证一下),这个文件可以为空,但是也可以存放一些初始化包的代码

from glance.api import *

从包api中导入所有,实际上该语句只会导入包api下__init__.py文件中定义的名字,我们可以在这个文件中定义__all___:

x=10

def func():
    print('from api.__init.py')

__all__=['x','func']
# __init__.py 文件的作用是将文件夹变为一个Python模块,Python 中的每个模块的包中,都有__init__.py 文件。  

# 通常__init__.py 文件为空,但是我们还可以为它增加其他的功能。我们在导入一个包时,实际上是导入了它的__init__.py文件。这样我们可以在__init__.py文件中批量导入我们所需要的模块,而不再需要一个一个的导入。

# __init__.py中还有一个重要的变量,__all__, 它用来将模块全部导入。

# py文件的汇编,只有在import语句执行时进行,当.py文件第一次被导入时,它会被汇编为字节代码,并将字节码写入同名的.pyc文件中。后来每次导入操作都会直接执行.pyc 文件(当.py文件的修改时间发生改变,这样会生成新的.pyc文件),在解释器使用-O选项时,将使用同名的.pyo文件,这个文件去掉了断言(assert)、断行号以及其他调试信息,体积更小,运行更快。(使用-OO选项,生成的.pyo文件会忽略文档信息)
# __init__.py
__all__ = ['os', 'sys', 're', 'urllib']
# a.py
from package import *

#这时就会把注册在__init__.py文件中__all__列表中的模块和包导入到当前文件中来。
#可以了解到,__init__.py主要控制包的导入行为。要想清楚理解__init__.py文件的作用,还需要详细了解一下import语句引用机制:

猜你喜欢

转载自my.oschina.net/u/3798913/blog/1808606