模块和包

参考:

6. 模块

Python 模块

5. 导入系统

每个.py文件都是一个模块,包则是多个模块的组合

模块加载

使用import语句加载并初始化模块,也可以使用from ... import ...语句仅加载模块中的某个变量、函数、类、子模块

import a
import a.b.c
import a.b.c as c
from a.b import c

使用as关键字后,其模块名重定义为c

模块重载

使用库importlibreload方法

def reload(module):

直接输入模块名即可

模块信息

获取模块名

可以在模块内部通过全局变量__name__获取

# 定义fibo.py
def print_module_name():
    print(__name__)
# 定义main.py
import module.fibo as fibo

if __name__ == '__main__':
    print(__name__)
    fibo.print_module_name()

输出如下:

__main__
module.fibo

当前调用模块(主模块)的模块名变成__main__

全局符号表/本地符号表

每个模块都有自己私有的全局符号表以及本地符号表,使用函数globals()locals()获取

import module.fibo as fibo

a = 3

def func():
    c = 5
    print(locals())

if __name__ == '__main__':
    b = 4
    print(globals())
    func()

全局/本地符号表都是一个字典

{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7fc805108f60>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/home/zj/pythonProjects/first/module/main.py', '__cached__': None, 'fibo': <module 'module.fibo' from '/home/zj/pythonProjects/first/module/fibo.py'>, 'a': 3, 'func': <function func at 0x7fc8051421e0>, 'b': 4}
{'c': 5}

注意 1:全局符号表包括全局变量、函数、模块信息

注意 2:本地符号表包括函数局部变量、方法等信息

如果在全局路径下调用方法locals,其作用和globals一样

模块搜索路径

加载import module时,解释器首先寻找内置模块是否匹配,然后按sys.path给出的目录列表进行搜索

import sys

print(sys.path)

在模块sys的属性path中按顺序保存有

  • 包含输入脚本的目录(或未指定文件时的当前目录)
  • 全局变量PYTHONPATH
  • python安装的默认设置
['/home/zj/pythonProjects/first/module', 
 '/home/zj/pythonProjects/first',
 '/home/zj/software/anaconda/anaconda3/lib/python37.zip',
 '/home/zj/software/anaconda/anaconda3/lib/python3.7', 
 '/home/zj/software/anaconda/anaconda3/lib/python3.7/lib-dynload', 
 '/home/zj/software/anaconda/anaconda3/lib/python3.7/site-packages', 
 '/home/zj/software/jetbrains/pycharm-2018.3.3/helpers/pycharm_matplotlib_backend']

同时还可以在运行过程中添加搜索路径

sys.path.append('../../adfa')

搜索名称列表

使用函数dir能够获取当前模块或指定模块的属性列表

def dir(p_object=None):

import module.fibo as fiboooo
import sys

a = 3

def func():
    c = 5
    print(locals())

if __name__ == '__main__':
    print(dir())
    print(dir(fiboooo))

如果没有输入参数,则返回当前模块的属性列表,包括定义的变量、函数、类、模块等信息

['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'fiboooo', 'func', 'sys']
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'fib', 'fib2', 'print_module_name']

标准模块属性

python内置函数和属性可通过检索模块builtins获取

import builtins

print(dir(builtins))
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']

参考:python中的模块、库、包有什么区别?

当同一目录下有多个模块,甚至其子目录下还有模块,可以在每层目录下创建文件__init__.py,解释器会将该层目录视为包,使用包能够更有效的管理模块

.
├── main.py
└── module
    ├── fibo.py
    ├── __init__.py
    └── test.py

module是一个包

加载包

可以使用import语句加载包中的模块

import module.fibo as fibo
import module.test as test

if __name__ == '__main__':
    fibo.fib(3)
    test.test()

__init__.py上实现模块加载,在其他模块上使用

import module.test
import module.fibo

上面实现代码等价如下

from module import *

if __name__ == '__main__':
    fibo.fib(3)
    test.test()

加载部分模块

如果只想一次性加载部分的模块,在__init__.py中使用变量__all__

__all__ = ['test']

如果执行加载语句from module import *仅能载入模块test

from module import *

if __name__ == '__main__':
    # fibo.fib(3)
    test.test()

子模块导入

当子包下的模块要引用另一个子包模块时,推荐使用绝对路径进行导入

├── main.py
└── module
    ├── fibo.py
    ├── __init__.py
    ├── sub1
    │   ├── __init__.py
    │   └── sub1_test.py
    ├── sub2
    │   ├── __init__.py
    │   └── sub2_test.py
    └── test.py

模块sub1_test引入模块sub2_test

# sub1_test
import module.sub2.sub2_test as test

def operation():
    test.operation()

if __name__ == '__main__':
    operation()
# sub2_test
def operation():
    print(__name__)

运行模块sub1_test.py,输出如下:

module.sub2.sub2_test

main.py中引入sub1_test.py,并执行opeation方法

import module.sub1.sub1_test as test

if __name__ == '__main__':
    test.operation()

输出如下:

module.sub2.sub2_test

__pychache__

运行完成后,在每个模块的路径下生成文件夹__pychache__,用于缓存模块编译版本,其命名格式为

module_name.python_version.pyc

缓存文件由python解释器自动生成,根据修改日期决定是否重编译,其目的是加快载入速度

└── module
    ├── fibo.py
    ├── __init__.py
    ├── __pycache__
    │   ├── fibo.cpython-37.pyc
    │   ├── __init__.cpython-37.pyc
    │   └── test.cpython-37.pyc
    ├── sub1
    │   ├── __init__.py
    │   ├── __pycache__
    │   │   ├── __init__.cpython-37.pyc
    │   │   └── sub1_test.cpython-37.pyc
    │   └── sub1_test.py
    ├── sub2
    │   ├── __init__.py
    │   ├── __pycache__
    │   │   ├── __init__.cpython-37.pyc
    │   │   └── sub2_test.cpython-37.pyc
    │   └── sub2_test.py
    └── test.py

pycharm使用注意

pycharm使用时需要设置当前文件夹为Source Root,否则无法载入当前目录下的其他.py文件

比如,工程目录如下:

├── docs
...
...
├── LICENSE
├── README.md
├── requirements.txt
└── src
    ├── __init__.py
    ├── BB.py
    ├── AA.py
    └── __pycache__

.py文件在src目录下,此时无法在AA.py文件中载入BB.py

from BB import BBBTest

需要右键src -> Mark Directory as -> Mark as source root