python 中 关于import的一些东东

1.python文件

        python源代码文件按照功能可以分为两种类型:

                1.用于执行的可执行程序文件

                2.不用于执行,仅用于被其它python源码文件导入的模块文件(通过import/from方式导入的py文件是模块文件)

__name__属性用来区分py文件是程序文件还是模块文件:

  • 当文件是程序文件的时候,该属性被设置为__main__
  • 当文件是模块文件的时候(也就是被导入时),该属性被设置为自身模块名

换句话说,__main__表示的是当前执行程序文件的默认模块名,想必学过其他支持包功能的语言的人很容易理解:程序都需要一个入口,入口程序所在的包就是main包,在main包中导入其它包来组织整个程序。python也是如此,只不过它是隐式自动设置的。

对于python来说,因为隐式自动设置,该属性就有了特殊妙用:直接在模块文件中通过if __name__ == "__main__"来判断,然后写属于执行程序的代码,如果直接用python执行这个文件,说明这个文件是程序文件,于是会执行属于if代码块的代码,如果是被导入,则是模块文件,if代码块中的代码不会被执行。

 2.模块

# b.py
x="var x in module b"
y=5

# a.py:
import b
import sys
print(b.x)
print(b.y)

a.py导入其它文件(b.py)后,就可以使用b.py文件中的属性(如变量、函数等)。这里,a.py就是可执行文件,b.py就是模块文件,但模块名为b,而非b.py。

python提供了一些标准库,是预定义好的模块文件,例如上面的sys模块。

在此有几个注意点,在后面会详细解释:

  1. 模块b的文件名为b.py,但import导入的时候,使用的名称为b,而非b.py
  2. a.py和b.py是在同一个目录下的,如果不在同目录下能否导入?
  3. 在a.py中访问b.py模块中的属性时,使用的是b.xb.y
  4. 上面都是直接以模块名导入的,python还支持更复杂的包导入方式,例如导入abc/b.py时,使用import abc.b。下一篇文章会详细解释包的导入方式

(1)python路径搜索

在a.py中导入模块b的时候,python会做一系列的模块文件路径搜索操作:b.py在哪里?只有找到它才能读取、运行(装载)该模块。

在任何一个python程序启动时,都会将模块的搜索路径收集到sys模块的path属性中(sys.path)。当python需要搜索模块文件在何处时,首先搜索内置模块,如果不是内置模块,则搜索sys.path中的路径列表,搜索时会从该属性列出的路径中按照从前向后的顺序进行搜索,并且只要找到就立即停止搜索该模块文件(也就是说不会后搜索的同名模块覆盖先搜索的同名模块)。

例如,在a.py文件中输出一下这个属性的内容:


# a.py:
import sys
print(sys.path)

 (2)修改搜索路径

               在import导入sys模块之后,可以修改sys.path,向这个列表中添加其它搜索路径,这样之后导入其它模块的时候,也会搜索该路径。sys.path的最后一项将是新添加的路径。


import sys
sys.path.append('xxxx')
print(sys.path)

 (3)模块导入细节

     python的import是在程序运行期间执行的,并非像其它很多语言一样是在编译期间执行。也就是说,import可以出现在任何地方,只有执行到这个import行时,才会执行导入操作。且在import某个模块之前,无法访问这个模块的属性。

python在import导入模块时,首先搜索模块的路径,然后编译并执行这个模块文件。

import b

print(b)
print(type(b))

 import导入模块时,搜索到模块文件b.py后:

        1.首先在内存中为每个待导入的模块构建module类的实例:模块对象。这个模块对象目前是空对象,这个对象的名称为全局变量b

        2.构造空模块实例后,将编译、执行模块文件b.py,并按照一定的规则将一些结果放进这个模块对象中

注意细节,编译、执行b.py、将结果保存到模块对象中。

模块第一次被导入的时候,会进行编译,并生成.pyc字节码文件,然后python执行这个pyc文件。当模块被再次导入时,如果检查到pyc文件的存在,且和源代码文件的上一次修改时间戳mtime完全对应(也就是说,编译后源代码没有进行过修改),则直接装载这个pyc文件并执行,不会再进行额外的编译过程。当然,如果修改过源代码,将会重新编译得到新的pyc文件。

注意,并非所有的py文件都会生成编译得到的pyc文件,对于那些只执行一次的程序文件,会将内存中的编译结果在执行完成后直接丢弃(多数时候如此,但仍有例外,比如使用compileall模块可以强制编译成pyc文件),但模块会将内存中的编译结果持久化到pyc文件中。另外,运行字节码pyc文件并不会比直接运行py文件更快,执行它也一样是一行行地解释、执行,唯一快的地方在于导入装载的时候无需重新编译而已。

执行模块文件(已完成编译)的时候,按照一般的执行流程执行:一行一行地、以代码块为单元执行。一般地,模块文件中只用来声明变量、函数等属性,以便提供给导入它的模块使用,而不应该有其他任何操作性的行为,比如print()操作不应该出现在模块文件中,但这并非强制。

总之,执行完模块文件后,这个模块文件将有一个自己的全局名称空间,在此模块文件中定义的变量、函数等属性,都会记录在此名称空间中。

最后,模块的这些属性都会保存到模块对象中。由于这个模块对象赋值给了模块变量b,所以通过变量b可以访问到这个对象中的属性(比如变量、函数等),也就是模块文件内定义的全局属性。

 (4)只导入一次

       假设a.py中导入了模块b和模块sys,在b.py中也导入了模块sys,但python默认对某个模块只会导入一次,如果a.py中先导入sys,再导入b,那么导入b并执行b.py的时候,会发现sys已经导入了,不会再去导入sys。

        如果某个程序文件中多次使用import(或from)导入同一个模块,虽然不会报错,但实际上还是直接使用内存中已装载好的模块对象。

        例如,b.py中x=3,导入它之后修改该值,然后再次导入,发现b.x并不会发生改变:

import b
print(b.x)   # 3

b.x=33
print(b.x)  # 33

import b 
print(b.x)  # 33

但是python提供了reload进行多次重复导入的方法

重载模块:imp.reload()

无论时import还是from,都只导入一次模块,但使用reload()可以强制重新装载模块。

reload()是imp模块中的一个函数,所以要使用imp.reload()之前,必须先导入imp。

from imp import reload
reload(b)

reload()会重新执行模块文件,并将执行得到的属性完全覆盖到原有的模块对象中。也就是说,reload()会重新执行模块文件,但不会在内存中建立新的模块对象,所以原有模块对象中的属性可能会被修改

(5)使用别名

        import导入时,可以使用as关键字指定一个别名作为模块对象的变量,例如:

        这时候模块对象将赋值给变量bb,而不是b,b此时不再是模块对象变量,而仅仅只是模块名。使用别名并不会影响性能,因为它仅仅只是一个赋值过程,只不过是从原来的赋值对象变量b变为变量bb而已。

import b as bb
from b import x as xx,y as yy
bb.x=3
print(bb.x)

(6)from导入部分属性

        除了import,还有一个from语句,表示从模块中导入部分指定的属性,且使得可以直接使用这些属性的名称来引用这些属性,而不需要加上模块变量名。例如原来import导入时访问变量x使用b.x,from导入时只需使用x即可。实际上,from导入更应该称为属性的再次赋值(拷贝)。        

        例如,b.py中定义了变量x、y、z,同时定义了函数f()和g(),在a.py中导入这个模块文件,但只导入x变量和f函数:

        

# a.py文件内容:
from b import x,f

print(x)
f()

# b.py文件内容:
x=3
y=4
z=5
def f():
    print("function f in b.py")

def g():
    print("function g in b.py")

        虽然from语句只导入模块的部分属性,但实际上仍然会完整地执行整个模块文件。

        同样的,from语句也可以指定导入属性的变量别名,例如,将b.py中的属性x赋值给xx,将y赋值给yy

        (7) from语句还有一个特殊导入统配符号*,它表示导入模块中的所有属性。

                多数时候,不应该使用from *的方式,因为我们可能会忘记某个模块中有哪些属性拷贝到了当前文件,特别是多个from *时可能会出现属性覆盖的问题

        (8) from导入的变量

# b.py:
x=3
y=[1,2]
z=5
def f():
    print("function f in b.py")

def g():
    print("function g in b.py")

# a.py:
from b import x,y,f

        实际上的行为是构造模块对象后,将这个模块对象对应的名称空间中的属性x、y和f重新赋值给a.py中的变量x、y和f,然后丢弃整个模块对象以及整个名称空间。换句话说,b不再是一个有效的模块变量(所以和import不一样),来自b的x,y,z,f和g也都被丢弃

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值