Python - 模块
概念
模块是最高级别的程序组织单元,模块对应于python程序文件。每一个文件都是一个模块
主要有以下操作:
- import
直接列出一个或多个需要加载的模块的名称,以逗号分割,因为它用一个名称引用整个模块对象 - from
会把特定的名称从一个文件复制到另一个作用域 - imp.reload
在不中终止python程序的情况下,重新载入模块文件
模块的作用
- 代码重用
- 系统命名空间的划分
- 实现共享服务和数据
导入模块的三种方式
包: 是一个包含多个模块的特殊目录
- 目录下有一个特殊的文件__init__.py
- 包的命名方式和 Pyhton 的标识符一致
- 作用: 当python文件较多时,方便目录管理维护
"""
说明:导入方式有很多,后面使用的时候,先查先用即可
方式1:
导入格式: import 包名.模块名
包名就是文件夹名 模块名就是文件里某个文件名字
使用格式: 包名.模块名.工具名 (类名、函数、变量)
"""
import my_message.send_message
my_message.send_message.send()
"""
方式2:
导入格式: from 包名 import 模块名
使用格式: 模块名.工具名 (类名、函数、变量)
"""
from my_message import send_message
send_message.send()
"""
方式3:
导入格式: from 包名.模块名 import 工具名
使用格式: 工具名 (类名、函数、变量)
"""
from hm_message.send_message import send
send()
import 如何工作
在Python ,导入并非只是把一个文件文本插入到另一个文件。导入其实是运行时的操作,程序第一次导入指定文件时,会执行三个步骤:
- 找到模块文件
- 编译成字节码
- 执行模块的代码来创建其所定义的对象
这三个步骤只在程序执行期间模块第一次导入时才会进行。在这之后导入相同模块时,会跳过这三个步骤,而只提取内存中已加载的模块对象。事实上,Python 把载入的模块存储到一个名为sys.modules的表中,并在每次导入操作的开始首先检查该表。如果该模块不存在,则启动这个三个步骤的过程。
1.搜索
Python 必须查找到import 语句所引用的模块文件。注意,import 语句使用的文件名中没有.py文件的扩展名,也没有目录路径,只有 import b,而不是 import c:\dir1\b.py。路径和后缀是刻意省略掉的,因为Python 使用了标准模块搜索路径(sys.path)来找出import 语句所对应的模块文件
2.编译
- 找到模块文件后,如果没有编译,会首先进行编译。
- 如果编译过,会检查字节码文件(.pyc)的时间戳,假如字节码文件比源码文件旧(修改过文件),就会自动重新生成字节码
- 如果.pyc文件不必对应的.py文件旧,就跳过编译步骤
字节码文件一般位于__pycache__ 目录中
注意:
只有被导入的文件才会在机器上留下.pyc。顶层文件的字节码是在内部使用后就丢弃了。被导入文件的字节码则保存在文件中从而可以之后导入的速度》
3.运行
从上到下依次执行模块编译的字节码文件,生成变量、函数、类。如果顶层代码确实做了实际工作,比如print,在导入的时候都会执行。默认情况这三步只有在第一次导入的时候才会执行,之后的导入会跳过这三个步骤,假如在模块已加载后还需要再次导入,需要调用reload方法
模块搜索路径
概括地讲,Python 的模块搜索路径是下面这些主要组件拼接而成的结果。其中有些进行了预先定义,而其中有些你可以进行调整来告诉Python 去哪里搜索:
- 程序的主目录
- PYTHONPATH(环境变量)
- 标准库目录
- %python_home%/ 或者%python_home%/Lib/site-packages 下的 *.pth结尾的文件 (PYTHONPATH 的替代方案),需要把相关路径一行一行的列出
- 第三方扩展应用的 site-packages主目录
模块的使用
import 语句
from 语句
from * 语句
导入只发生一次
因为模块中的顶层程序代码通常只执行一次,你可以借此对变量进行初始化。例如,考虑下面的文件simple.py:
print('hello')
spam = 1
print和 = 语句在第一次被导入时执行,而变量spam 也在导入时被初始化:
>>> import simple
hello
>>> simple.spam
1
第二次及之后的导入并不会重新执行该模块的代码,只是从Python内部模块表中取出已创建的模块对象。因此,变量spam不会被再次初始化:
>>> simple.spam = 2
>>> import simple
>>> simple.spam
2
跨文件修改变量
# small.py
x = 1
y = [1,2,3]
>>> from small import x,y
>>> x = 42 # x 是不可变对象
>>> y [0] = 42
>>> import small
>>> small.x # 未被修改
1
>>> small.y # 被修改
[42, 2, 3]
>>> small.x = 43 # 修改small 中的x,是比较坏的设计
>>> x
42
import 和 from 的等价性
# 一个像这个的import 语句
from moudle import name1,name2
# 与下面这些语句是等效的
import moudle
name1 = moudel.name1
name2 = moudel.name2
混合使用模式:__name__
和__main__
每个模块都有一个名为__name__的内置属性,Python会遵循下面的规则自动创建并赋值该属性:
- 如果文件作为顶层程序文件执行,在启动时__name__ 会被设置为字符串"main"
- 如果文件被导入,__name__就会改设成客户程序所了解的模块名
绝对导入和 相对导入
绝对导入:import t1.a 或者 from t1 import a 等。
相对导入:from . import a 或者 from .. import a 或者 from ..t1 import a 或者 from .t1 import a等。
相对路径导包报错
报错:
ImportError: attempted relative import with no known parent package, 识别不到父包
原因:
相对导入通过使用模块的__name__
属性来确定模块在包层次结构中的位置。如果该模块的名称不包含任何包信息(例如,它被设置为 main ),那么相对引用会认为这个模块就是顶级模块,而不管模块在文件系统上的实际位置。python解释器没有关于模块所属的包的任何信息( __name__ = __main__ 和 __package__ = None
),因此它抛出了找不到父级包的异常。
解决办法一:
需在项目顶层包的同及目录下执行:
# 注意是在项目顶层包的同及目录下执行,否在会报错 :ImportError: attempted relative import beyond top-level package
PS E:\PyProject\hhhhhhh\interface\day06> python -m hol.stu.test_02
解决办法二:
将父路径临时添加到sys.path
参考:
https://blog.csdn.net/ybw_2569/article/details/98775794
https://blog.csdn.net/weixin_45331210/article/details/98745748
https://www.cnblogs.com/rrh4869/p/11175344.html
python xxx.py 和 python -m xxx 的区别?
python --help 查看-m 的意思:
-m mod : run library module as a script (terminates option list)
主要区别
执行时sys.path不同:
python xxx.py :将该脚本所在目录添加至sys.path
python -m xxx :将当前运行命令的路径添加至sys.path (从而解决找不到不在同一个包导入模块的问题)
python -m 的其他用法:
https://blog.csdn.net/chinesehuazhou2/article/details/102996709
导包案例
包结构:
# spam.py
import sys
print('spam 的__name__', __name__)
import mypackage.A.grok
for a in sys.path:
print(a)
if __name__ == '__main__':
print('xxxxxxxxxxxxx')
# grok.py
print('grok....run')
from ..B import bar
print('grok 的__name__', __name__)
# bar.py
print('bar...run')
print('bar 的__name__', __name__)
# out:
"""
spam 的__name__ __main__
grok....run
bar...run
bar 的__name__ mypackage.B.bar
grok 的__name__ mypackage.A.grok
E:\PyProject\pythonProject1\dir_demo\mypackage\A
E:\PyProject\pythonProject1\dir_demo
E:\code_tool\python\python310.zip
E:\code_tool\python\DLLs
E:\code_tool\python\lib
E:\code_tool\python
E:\PyProject\py_basic
E:\PyProject\py_basic\lib\site-packages
xxxxxxxxxxxxx
"""
总结:
a) 导入模块,只能在sys.path中包含的路径下去找要导入的模块。b)导入包,可以采用直接路径或者相对路径。顶层模块(被运行的模块)可以直接导入同级目录下的其他子包或子模块,或者通过绝对路径导入其他子包或子模块。被顶层模块通过绝对路径调用的子包或子模块,可以在子包或子模块内部通过相对路径的方式导入其他包或者模块。
c) 一个包中的某个模块的__name__如果包含了完整结构,如mypackage.A.grok,则在这个模块里可以使用相对路径,否则不可以。顶层模块的__name__一直是__main__,所以都不可以使用相对路径导入。
pycharm 运行与命令行运行python文件的区别
pycharm 会在sys.path 路径添加项目的根目录,而命令行不会
命令行运行:
pycharm 运行:
本文来自博客园,作者:chuangzhou,转载请注明原文链接:https://www.cnblogs.com/czzz/p/15758277.html