Python模块(Python Module)

一般来说,在编程语言中。包、库、模块都是指同一个概念,是一种代码的组织方式,将一种功能或若干种封装起来,供自己或他人使用。

Python中只有一种组织方式,称为模块。

module

  • Python中的模块module,指的是Python源代码文件
  • python中的一个类。模块导入后,会实例化一个module对象方便使用

package

  • 包package是一种特殊的module
  • 为了便利模块的组织,使用文件夹(目录)加__init__.py文件来作为包,包也是一个模块,文件夹中__init__.py的内容则视为是包的内容。在包中的其他源代码文件则为包的子模块。

import

在python中,可以使用import语句来导入模块,导入后会生成一个模块对象绑定到本地的名词空间中,对象拥有模块中所有的对象。可以通过点运算符来使用。

python中只要一个带有__init__.py的文件夹或者一个.py文件都视为是一个模块。都可以通过import语句导入。import有以下几种用法。

测试模块的文件结构

m
+--m1
	+--__init__.py
	+--m2
		+--__init__.py
+--__init__.py
+--test.py

#不带后缀的为包,并且都带有各自的__init__.py文件
m - __init__.py
x = 100 在m模块中有一个属性x=100

导入方式

  • import module
print(dir())
import m

print(dir())
print(type(m))
print(m.x)


#['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
#['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'm']
# <class 'module'>
#100

可以看到导入后本地名词空间中增加了一个新对象,与模块名相同。并且可以使用点运算符访问到模块中的成员。

  • import module as name
print(dir())
import m as alias

print(dir())
print(type(alias))
print(alias.x)

# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'alias']
# <class 'module'>
#100

增加到名词空间中的对象名称为as后的名称。使用效果与直接导入相同,原名称m并不在其中。

  • import package.module
import m.test

print(dir())
print(m.test)

# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'm']
# <module 'm.test' from 'D:\\PythonRoot\\module_test\\m\\test.py'>

注意:import只能导入模块,即无法通过import module.attribute来导入模块中的属性。

这里可以发现即使使用了import m.test,绑定的名词依旧是m。那么这样与直接导入m有什么差异呢?

import m

print(dir())
print(m.test)

# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'm']
#!AttributeError: module 'm' has no attribute 'test'

发现只导入顶级模块的话,直接使用顶级模块下的其他模块就会引发AttributeError属性异常。不存在test属性,即test模块没有被导入。

import m.test


print(m.test)
print(m.m1)

#<module 'm.test' from 'D:\\PythonRoot\\module_test\\m\\test.py'>
#!AttributeError: module 'm' has no attribute 'm1'

使用import m.test发现,只有test模块被加载了,与test同级的m1没有被加载

import m.m1.m2


print(m)
print(m.m1)
print(m.m1.m2)


# <module 'm' from 'D:\\PythonRoot\\module_test\\m\\__init__.py'>
# <module 'm.m1' from 'D:\\PythonRoot\\module_test\\m\\m1\\__init__.py'>
# <module 'm.m1.m2' from 'D:\\PythonRoot\\module_test\\m\\m1\\m2\\__init__.py'>

导入子模块时,父模块也会被导入

  • import package.package.module as name
import m.m1.m2 as alias
  
print(alias)
  
  
#<module 'm.m1.m2' from 'D:\\PythonRoot\\module_test\\m\\m1\\m2\\__init__.py'>

可以通过as name的方式,直接将模块绑定到名词上,方便使用。

总结

  • 通过import导入模块后会生成一个模块对象,可以通过模块对象访问到其中的对象。
  • import只能导入模块
  • 加载子模块,则一定会加载其父模块
  • import分两步,加载和将名词导入。在加载时,只会加载显式导入的模块,例如import m.test只会加载m模块和test模块,虽然名词空间中只有m,但是可以使用m.test访问到test模块。import m只会加载m模块,使用m.test时则会导致异常。
  • 在名词导入时,默认只会将顶级模块名导入,使用as则会把模块绑定到指定的名词并导入。

from import

在Python中,为了方便使用还提供了另一种导入方式,即from import

用法

  • from package import module
from m import test

print(dir())

#['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'test']

通过这种方式能直接把test模块对象的名称直接导入到当前的名词空间中,相当于import m.test as test,这里的package可以是非顶级的包。即可以用import m.m1 import m2的方式来导入m2模块。

  • from module import attribute
from m import x

print(dir())
print(x)

# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'x']
# 100

可以使用这种方式直接导入模块中的属性

  • from import 能够导入模块或者属性,那么如果在一个模块下有一个子模块和其属性同名呢?

在m的__init__.py文件中加入test = 'attribute m.test'

from m import test

print(test)

#attribute m.test

可以发现导入对象并不是一个模块,即from import会优先导入非模块的属性

总结

  • from import能够导入模块或者属性,并且会将导入的模块或者属性直接加入到本地名词空间中
  • from后必须是一个模块,语句无法从其他对象导入属性
  • 如果有同名的属性和模块,会优先导入非模块属性

模块对象属性

在Python3之中,万物皆对象,module也是一个对象,可以通过其 __dict__来查看其属性。

import m

print(m.__dict__.keys())

#dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__path__', '__file__', '__cached__', '__builtins__', 'x', 'test'])

发现除了x和test自己添加的属性外,默认还带有许多属性。

变量名类型作用
__name__str模块名
__doc__str文档字符串
__package__str所在的包名,如果是包,则是自身名称
__spec___frozen_importlib.ModuleSpec类实例模块使用的标准
__path__list包的文件夹所在的路径。只有包有该属性。
__file__str文件的路径。如果是包,则是__init__.py文件的 路径。
__cached__str记录模块编译后生成.pyc文件的位置

查找顺序和加载方式

Python中使用一个list来存储搜索模块时的路径,优先级从低到高依次降低。从索引0开始查找,只要找到了就停止,找不到返回importError异常。

sys.path

sys.path中存放了查找模块时所用到的路径。是一个有序的可写的list,可以使用list操作往其中增加或删减路径。

注意:sys.path必须存放绝对路径

sys.module

在m模块中加入打印语句print(__name__)

import m
import m

#m

可以发现打印语句只执行了一次,说明在Python中存在一个导入机制,不会导入重复的模块。

Python将导入的模块放到了sys.module,是一个dict类型。解释器在运行时,发现要加载的模块已经加载过了,就只使用一个名称来绑定这个模块,不再重新导入。我们可以也可以使用这个字典直接来访问已经加载过的模块。

主模块

  • 程序中的第一个入口,其他模块皆为该模块直接或间接加载
  • 运行的第一个.py文件
  • __name__属性名为'__main__'
if __name__ == '__main__':
    pass

利用这一特性,可以用上面语法来写模块的测试语句。只有当此模块为主模块时才会执行其中语句。

绝对导入和相对导入

绝对导入

文章至此,使用的都是绝对导入,使用一个模块名,从sys.path中搜索路径导入的方式都是绝对导入。

相对导入
  • 相对导入只能在from import中使用
  • 只能在一个包内使用
  • 使用相对导入后的模块无法作为主模块来使用
用法

与绝对路径相似,但在from后使用相对路径

符号含义
.当前目录
..上级目录
...上上级目录

比如可以在m2中使用from ... import test来访问m下的test模块,也可以使用from ..package import module来导入同级目录包内的模块,from .module import attribute导入同级目录下模块属性。

需要注意的是,如果是包,是以__init__.py文件位置为准,并非文件夹。

from import *

  • from import可以使用from module import *的方式来简易得导入多个属性

  • 默认只会导入非下划线开头的变量名,不会导入模块,如果导入的模块中显式地导入了其他模块,且导入模块使用的变量名非下划线开头,该模块对象也会被导入。

  • __all__属性

    • 模块默认没有定义__all__属性,当没有该属性时,from import *只会导入非下划线开头以及非模块属性。
    • __all__是一个可迭代对象,常用tuple和list
    • 如果定义了__all__from import *只会导入其中的变量,并且不管是否是模块(这里的模块指的是未被加载的子模块)或是下划线开头。
Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐