天天看点

Python模块化Python模块(Python Module)

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 *

      只会导入其中的变量,并且不管是否是模块(这里的模块指的是未被加载的子模块)或是下划线开头。