热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

python基础(十九):模块

下面是小凰凰的简介,看下吧!💗人生态度:珍惜时间,渴望学习,热爱音乐,把握命运&

下面是小凰凰的简介,看下吧!
💗人生态度:珍惜时间,渴望学习,热爱音乐,把握命运,享受生活
💗学习技能:网络 -> 云计算运维 -> python全栈( 当前正在学习中)
💗您的点赞、收藏、关注是对博主创作的最大鼓励,在此谢过!
有相关技能问题可以写在下方评论区,我们一起学习,一起进步。
后期会不断更新python全栈学习笔记,秉着质量博文为原则,写好每一篇博文。


文章目录

    • 一、模块介绍
    • 二、模块的使用
        • 1、import语句
            • (1)基本用法
            • (2)三种模块
        • 2、from ... import ... 语句
            • (1)基本用法
            • (2)from 模块名 import *
        • 3、其他导入语法(as别名)
        • 4、循环导入问题
            • (1)测试一(采用执行run.py文件)
            • (2)测试二(采用执行两个模块文件中其中之一)
            • (3)循环导入报错的解决方案
        • 5、搜索模块的路径与优先级
            • (1)模块查询及sys.path的介绍
            • (2)sys.path的使用
        • 6、区分py文件的两种用途(__name__变量)
        • 7、编写一个规范的模块


一、模块介绍

在Python中,一个py文件就是一个模块,文件名为xxx.py模块名则是xxx,导入模块可以引用模块中已经写好的功能。

将程序模块化会使得程序的组织结构清晰,维护起来更加方便。比起直接开发一个完整的程序,单独开发一个小的模块也会更加简单,并且程序中的模块与电脑中的零部件稍微不同的是:程序中的模块可以被重复使用。所以总结下来,使用模块既保证了代码的重用性,又增强了程序的结构性和可维护性。另外除了自定义模块外,我们还可以导入使用内置或第三方模块提供的现成功能,这种“拿来主义”极大地提高了程序员的开发效率。

二、模块的使用


1、import语句


(1)基本用法

有如下示范文件

#文件名:foo.py
x=1
def get():print(x)
def change():global xx=0

要想在另外一个py文件中引用foo.py中的功能,需要使用import foo,首次导入模块会做三件事:

  • 执行源文件代码
  • 产生一个新的名称空间用于存放源文件执行过程中产生的名字
  • 在当前执行文件所在的名称空间中得到一个名字foo,该名字指向新创建的模块名称空间,若要引用模块名称空间中的名字,需要加上该前缀,如下

#本程序文件名为test.py
import foo #导入模块foo
a=foo.x #引用模块foo中变量x的内存地址赋值给当前名称空间中的名字a
foo.get() #调用模块foo的get函数
foo.change() #调用模块foo中的change函数

import foo 此代码执行后的内存图解:
在这里插入图片描述加上foo.作为前缀就相当于指名道姓地说明要引用foo名称空间中的名字,所以肯定不会与当前执行文件所在名称空间中的名字相冲突,并且若当前执行文件的名称空间中存在x,执行foo.get()或foo.change()操作的都是源文件中的全局变量x。

需要强调一点是,一个程序文件中第一次导入模块已经将其加载到内存空间了,此次导入之后的重复导入会直接引用内存中已存在的模块,不会重复执行文件,通过import sys,打印sys.modules的值可以看到内存中已经加载的模块名。

提示:

#1、在Python中模块也属于第一类对象,可以进行赋值、以数据形式传递以及作为容器类型的元素等操作。
#2、模块名应该遵循小写形式,标准库从python2过渡到python3做出了很多这类调整,比如ConfigParser、Queue、SocketServer全更新为纯小写形式。

(2)三种模块

# 三种模块的导入顺序(约定俗成)
1. python内置模块
2. 第三方模块
3. 程序员自定义模块#例如
import sys
import timeimport 第三方模块1
import 第三方模块2import 自定义模块1
import 自定义模块2

​ 当然,我们也可以在函数内导入模块,对比在文件开头导入模块属于全局作用域,在函数内导入的模块则属于局部的作用域。

2、from … import … 语句


(1)基本用法

from…import…与import语句基本一致,唯一不同的是:使用import foo导入模块后,引用模块中的名字都需要加上foo.作为前缀,而使用from foo import x,get,change,Foo则可以在当前执行文件中直接引用模块foo中的名字,如下:

from foo import x,get,change #将模块foo中的x和get导入到当前名称空间
a=x #直接使用模块foo中的x赋值给a
get() #直接执行foo中的get函数
change() #即便是当前有重名的x,修改的仍然是源文件中的x

from foo import x,get,change此代码执行后的内存图解:
在这里插入图片描述

#文件名:foo.py
x=1
def get():print(x)
def change():global xx=0#文件名:test.py
from foo import x,get,change
print(x) # 执行结果:1
change() #改变x值为0
print(x) # 执行结果:1
from foo import x
print(x) # 执行结果:0#模块中的x值改变,并不影响test.py中的x指向1的内存地址,这两个x不是一个x了。除非重新导入模块,才会改变x的值。

无需加前缀的好处是使得我们的代码更加简洁,坏处则是容易与当前名称空间中的名字冲突,如果当前名称空间存在相同的名字,则后定义的名字会覆盖之前定义的名字。

(2)from 模块名 import *

#foo.py
__all__=['x','get'] #该列表中所有的元素必须是字符串类型,每个元素对应foo.py中的一个名字。在列表中填入,from foo import * 允许导入的此文件的名字,默认列表中全部名字都有!
x=1
def get():print(x)
def change():global xx=0
class Foo:def func(self):print('from the func')#test.py文件
from foo import * #默认是把foo中所有的名字都导入到当前执行文件的名称空间中,在当前位置直接可以使用这些名字,但是你可以通过在模块中改变__all__变量中列表的值,达到控制*号导入部分名字,而不是全部名字a=x #可用
get() #可用
change() #不可用

3、其他导入语法(as别名)

我们还可以在当前位置为导入的模块起一个别名

import foo as f #为导入的模块foo在当前位置起别名f,以后再使用时就用这个别名f
f.x
f.get()

还可以为导入的一个名字起别名

from foo import get as get_x
get_x()

别名的优点:通常在被导入的名字过长时采用起别名的方式来1、精简代码,另外为被导入的名字2、起别名可以很好地避免与当前名字发生冲突,还有很重要的一点就是:3、可以保持调用方式的一致性,例如我们有两个模块json和pickle同时实现了load方法,作用是从一个打开的文件中解析出结构化的数据,但解析的格式不同,可以用下述代码有选择性地加载不同的模块,第3个优点的代码解释:

if data_format == 'json':import json as serialize #如果数据格式是json,那么导入json模块并命名为serialize
elif data_format == 'pickle':import pickle as serialize #如果数据格式是pickle,那么导入pickle模块并命名为serializedata=serialize.load(fn) #最终调用的方式是一致的

4、循环导入问题

循环导入问题指的是在一个模块加载/导入的过程中导入另外一个模块,而在另外一个模块中又返回来导入第一个模块中的名字,由于第一个模块尚未加载完毕,所以引用失败、抛出异常,
我们以下述文件为例,来详细分析循环/嵌套导入出现异常的原因以及解决的方案

m1.py

print('正在导入m1')
from m2 import yx='m1'

m2.py

print('正在导入m2')
from m1 import xy='m2'

run.py

import m1

(1)测试一(采用执行run.py文件)

#1、执行run.py会抛出异常
正在导入m1
正在导入m2
Traceback (most recent call last):File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/aa.py", line 1, in <module>import m1File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module>from m2 import yFile "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m2.py", line 2, in <module>from m1 import x
ImportError: cannot import name &#39;x&#39;#2、分析
先执行run.py--->执行import m1&#xff0c;开始导入m1并运行其内部代码--->打印内容"正在导入m1"
--->执行from m2 import y 开始导入m2并运行其内部代码--->打印内容“正在导入m2”--->执行from m1 import x,由于m1已经被导入过了&#xff0c;所以不会重新导入&#xff0c;所以直接去m1中拿x&#xff0c;然而x此时并没有存在于m1中&#xff0c;所以报错

&#xff08;2&#xff09;测试二&#xff08;采用执行两个模块文件中其中之一&#xff09;

#1、执行文件不等于导入文件&#xff0c;比如执行m1.py不等于导入了m1
直接执行m1.py抛出异常
正在导入m1
正在导入m2
正在导入m1
Traceback (most recent call last):File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module>from m2 import yFile "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m2.py", line 2, in <module>from m1 import xFile "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module>from m2 import y
ImportError: cannot import name &#39;y&#39;#2、分析
执行m1.py&#xff0c;打印“正在导入m1”&#xff0c;执行from m2 import y &#xff0c;导入m2进而执行m2.py内部代码--->打印"正在导入m2"&#xff0c;执行from m1 import x&#xff0c;此时m1是第一次被导入&#xff0c;执行m1.py并不等于导入了m1&#xff0c;于是开始导入m1并执行其内部代码--->打印"正在导入m1"&#xff0c;执行from m1 import y&#xff0c;由于m1已经被导入过了&#xff0c;所以无需继续导入而直接问m2要y&#xff0c;然而y此时并没有存在于m2中所以报错

总的来说&#xff1a;无论是测试一还是测试二&#xff0c;报错原因都一样&#xff0c;就是两个模块相互导&#xff0c;因为都想要对方导入模块之后的名字&#xff0c;因此两个模块文件都卡在了import语句哪里&#xff0c;永远运行不到后面的变量赋值&#xff0c;两个测试唯一的不同&#xff1a;在于直接导入模块文件而报错和执行模块文件而报错的区别

&#xff08;3&#xff09;循环导入报错的解决方案

# 方案一&#xff1a;导入语句放到最后&#xff0c;保证在导入时&#xff0c;所有名字都已经加载过
# 文件&#xff1a;m1.py
print(&#39;正在导入m1&#39;)x&#61;&#39;m1&#39;from m2 import y# 文件&#xff1a;m2.py
print(&#39;正在导入m2&#39;)
y&#61;&#39;m2&#39;from m1 import x# 文件&#xff1a;run.py内容如下&#xff0c;执行该文件&#xff0c;可以正常使用
import m1
print(m1.x)
print(m1.y)

# 方案二&#xff1a;导入语句放到函数中&#xff0c;只有在调用函数时才会执行其内部代码&#xff0c;定义阶段只检查语法问题。
# 文件&#xff1a;m1.py
print(&#39;正在导入m1&#39;)def f1():from m2 import yprint(x,y)x &#61; &#39;m1&#39;# 文件&#xff1a;m2.py
print(&#39;正在导入m2&#39;)def f2():from m1 import xprint(x,y)y &#61; &#39;m2&#39;# 文件&#xff1a;run.py内容如下&#xff0c;执行该文件&#xff0c;可以正常使用
import m1m1.f1()# 注意&#xff1a;函数解决方案&#xff0c;也有局限性&#xff0c;因为模块在函数中导入&#xff0c;那么模块便只能局部使用。

循环导入问题大多数情况是因为程序设计失误导致&#xff0c;上述解决方案也只是在烂设计之上的无奈之举&#xff0c;在我们的程序中应该尽量避免出现循环/嵌套导入&#xff0c;如果多个模块确实都需要共享某些数据&#xff0c;可以将共享的数据集中存放到某一个地方&#xff0c;然后进行导入

5、搜索模块的路径与优先级

模块其实分为四个通用类别&#xff0c;分别是&#xff1a;

1.使用纯Python代码编写的py文件2.包含一系列模块的包3.使用C编写并链接到Python解释器中的内置模块4.使用C或C&#43;&#43;编译的扩展模块

&#xff08;1&#xff09;模块查询及sys.path的介绍

在导入一个模块时&#xff0c;如果该模块已加载到内存中&#xff0c;则直接引用&#xff0c;否则会优先查找内置模块&#xff0c;然后按照从左到右的顺序依次检索sys.path中定义的路径&#xff0c;直到找模块对应的文件为止&#xff0c;否则抛出异常。sys.path也被称为模块的搜索路径&#xff0c;它是一个列表类型

>>> import sys
>>> sys.path
[&#39;&#39;, &#39;/Library/Frameworks/Python.framework/Versions/3.7/lib/python37.zip&#39;, &#39;/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7&#39;, &#39;/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload&#39;, &#39;/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages&#39;]

列表中的每个元素其实都可以当作一个目录来看&#xff1a;在列表中会发现有.zip或.egg结尾的文件&#xff0c;二者是不同形式的压缩文件&#xff0c;事实上Python确实支持从一个压缩文件中导入模块&#xff0c;我们也只需要把它们都当成目录去看即可

&#xff08;2&#xff09;sys.path的使用

sys.path中的第一个路径通常为空&#xff0c;代表执行文件所在的路径(执行文件执行时&#xff0c;None被赋值成执行文件所在的路径)&#xff0c;所以在被导入模块与执行文件在同一目录下时肯定是可以正常导入的&#xff0c;而针对被导入的模块与执行文件在不同路径下的情况&#xff0c;为了确保模块对应的源文件仍可以被找到&#xff0c;需要将源文件foo.py所在的路径添加到sys.path中&#xff0c;假设foo.py所在的路径为/pythoner/projects/

import sys
sys.path.append(r&#39;/pythoner/projects/&#39;) #也可以使用sys.path.insert(……)import foo #无论foo.py在何处,我们都可以导入它了

6、区分py文件的两种用途&#xff08;__name__变量&#xff09;

一个Python文件有两种用途&#xff0c;一种被当主程序/脚本执行&#xff0c;另一种被当模块导入&#xff0c;为了区别同一个文件的不同用途&#xff0c;每个py文件都内置了__name__变量&#xff0c;该变量在py文件被当做脚本执行时赋值为“__main__”,在py文件被当做模块导入时赋值为模块名

#foo.py
x&#61;1
def get():print(x)
def change():global xx&#61;0if __name__ &#61;&#61; &#39;__main__&#39;:# foo.py被当做脚本执行时运行的代码,一般写测试模块功能的代码change()get()
else:# foo.py被当做模块导入时运行的代码&#xff0c;一般这里什么都不用干pass

通常我们会在if的子代码块中编写针对模块功能的测试代码&#xff0c;这样foo.py在被当做脚本运行时&#xff0c;就会执行测试代码&#xff0c;而被当做模块导入时则不用执行测试代码。

总结&#xff1a;

__name__属性可以显示一个模块的某功能是被自己执行还是被别的文件调用执行&#xff0c;假设模块A、B&#xff0c;模块A自己定义了功能C,模块B调用模块A&#xff0c;现在功能C被执行了&#xff1a;如果C被A自己执行&#xff0c;也就是说模块执行了自己定义的功能&#xff0c;那么 __name__&#61;&#61;&#39;__main__&#39;如果C被B调用执行&#xff0c;也就是说当前模块调用执行了别的模块的功能&#xff0c;那么__name__&#61;&#61;&#39;A&#39;&#xff08;被调用模块的名字&#xff09;

7、编写一个规范的模块

我们在编写py文件时&#xff0c;需要时刻提醒自己&#xff0c;该文件既是给自己用的&#xff0c;也有可能会被其他人使用&#xff0c;因而代码的可读性与易维护性显得十分重要&#xff0c;为此我们在编写一个模块时最好按照统一的规范去编写&#xff0c;如下

#!/usr/bin/env python #通常只在类unix环境有效,作用是可以使用脚本名来执行&#xff0c;而无需直接调用解释器。"The module is used to..." #模块的文档描述import sys #导入模块x&#61;1 #定义全局变量,如果非必须,则最好使用局部变量,这样可以提高代码的易维护性,并且可以节省内存提高性能class Foo: #定义类,并写好类的注释&#39;Class Foo is used to...&#39;passdef test(): #定义函数,并写好函数的注释&#39;Function test is used to…&#39;passif __name__ &#61;&#61; &#39;__main__&#39;: #主程序test() #在被当做脚本执行时,执行此处的代码


推荐阅读
  • Requests库的基本使用方法
    本文介绍了Python中Requests库的基础用法,包括如何安装、GET和POST请求的实现、如何处理Cookies和Headers,以及如何解析JSON响应。相比urllib库,Requests库提供了更为简洁高效的接口来处理HTTP请求。 ... [详细]
  • OBS Studio自动化实践:利用脚本批量生成录制场景
    本文探讨了如何利用OBS Studio进行高效录屏,并通过脚本实现场景的自动生成。适合对自动化办公感兴趣的读者。 ... [详细]
  • Jupyter Notebook多语言环境搭建指南
    本文详细介绍了如何在Linux环境下为Jupyter Notebook配置Python、Python3、R及Go四种编程语言的环境,包括必要的软件安装和配置步骤。 ... [详细]
  • 本文详细介绍了如何在VSCode中正确配置Python环境,包括通过不同方式安装Python以及如何安装和配置Python插件,确保开发环境顺利搭建。 ... [详细]
  • 解决Pytesser模块在Windows环境下出现的错误
    本文详细探讨了如何解决在Windows环境中使用Pytesser模块进行OCR(光学字符识别)时遇到的WindowsError错误,提供了具体的解决方案。 ... [详细]
  • 本文探讨了如何通过优化 DOM 操作来提升 JavaScript 的性能,包括使用 `createElement` 函数、动画元素、理解重绘事件及处理鼠标滚动事件等关键主题。 ... [详细]
  • 本文详细介绍了JQuery Mobile框架中特有的事件和方法,帮助开发者更好地理解和应用这些特性,提升移动Web开发的效率。 ... [详细]
  • 本文详细介绍了如何利用 Bootstrap Table 实现数据展示与操作,包括数据加载、表格配置及前后端交互等关键步骤。 ... [详细]
  • 本文详细介绍了笔记本电脑上多种实用的快捷键,包括屏幕调整、图形设置、分辨率更改、驱动更新、导航操作、音量控制及屏幕锁定等,旨在帮助用户更高效地使用笔记本电脑。 ... [详细]
  • 本文介绍了在Linux环境下如何有效返回命令行状态、上一级目录及快速查找头文件和函数定义的方法。包括处理长时间运行命令、编辑器退出技巧、目录导航以及文件搜索策略。 ... [详细]
  • Bootstrap Paginator 分页插件详解与应用
    本文深入探讨了Bootstrap Paginator这款流行的JavaScript分页插件,提供了详细的使用指南和示例代码,旨在帮助开发者更好地理解和利用该工具进行高效的数据展示。 ... [详细]
  • 在Python编程中,经常需要处理文件下载的任务。本文将介绍三种常用的下载方法:使用urllib、urllib2以及requests库进行HTTP请求下载,同时也会提及如何通过ftplib从FTP服务器下载文件。 ... [详细]
  • 如何高效解决Android应用ANR问题?
    本文介绍了ANR(应用程序无响应)的基本概念、常见原因及其解决方案,并提供了实用的工具和技巧帮助开发者快速定位和解决ANR问题,提高应用的用户体验。 ... [详细]
  • 本文详细介绍了如何在Linux系统中使用nslookup命令查询DNS服务器地址,这对于Linux服务器的运维管理是非常重要的基础知识。 ... [详细]
  • 基于iSCSI的SQL Server 2012群集测试(一)SQL群集安装
    一、测试需求介绍与准备公司计划服务器迁移过程计划同时上线SQLServer2012,引入SQLServer2012群集提高高可用性,需要对SQLServ ... [详细]
author-avatar
惰费旧使爽DDD_540
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有