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 ,导入并非只是把一个文件文本插入到另一个文件。导入其实是运行时的操作,程序第一次导入指定文件时,会执行三个步骤:

  1. 找到模块文件
  2. 编译成字节码
  3. 执行模块的代码来创建其所定义的对象

这三个步骤只在程序执行期间模块第一次导入时才会进行。在这之后导入相同模块时,会跳过这三个步骤,而只提取内存中已加载的模块对象。事实上,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。顶层文件的字节码是在内部使用后就丢弃了。被导入文件的字节码则保存在文件中从而可以之后导入的速度》

字节码文件介绍:https://zhuanlan.zhihu.com/p/39259061

3.运行

从上到下依次执行模块编译的字节码文件,生成变量、函数、类。如果顶层代码确实做了实际工作,比如print,在导入的时候都会执行。默认情况这三步只有在第一次导入的时候才会执行,之后的导入会跳过这三个步骤,假如在模块已加载后还需要再次导入,需要调用reload方法

模块搜索路径

概括地讲,Python 的模块搜索路径是下面这些主要组件拼接而成的结果。其中有些进行了预先定义,而其中有些你可以进行调整来告诉Python 去哪里搜索:

  1. 程序的主目录
  2. PYTHONPATH(环境变量)
  3. 标准库目录
  4. %python_home%/ 或者%python_home%/Lib/site-packages 下的 *.pth结尾的文件 (PYTHONPATH 的替代方案),需要把相关路径一行一行的列出
  5. 第三方扩展应用的 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

https://blog.csdn.net/qq_31334901/article/details/107632488?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~default-1.pc_relevant_default&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~default-1.pc_relevant_default&utm_relevant_index=2

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 运行:

https://www.jianshu.com/p/a6b8a09b929a

posted @ 2022-01-02 20:45  chuangzhou  阅读(289)  评论(0编辑  收藏  举报