热门标签 | 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() #在被当做脚本执行时,执行此处的代码


推荐阅读
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 本文介绍了如何使用python从列表中删除所有的零,并将结果以列表形式输出,同时提供了示例格式。 ... [详细]
  • 本文介绍了在使用Python中的aiohttp模块模拟服务器时出现的连接失败问题,并提供了相应的解决方法。文章中详细说明了出错的代码以及相关的软件版本和环境信息,同时也提到了相关的警告信息和函数的替代方案。通过阅读本文,读者可以了解到如何解决Python连接服务器失败的问题,并对aiohttp模块有更深入的了解。 ... [详细]
  • Ubuntu安装常用软件详细步骤
    目录1.GoogleChrome浏览器2.搜狗拼音输入法3.Pycharm4.Clion5.其他软件1.GoogleChrome浏览器通过直接下载安装GoogleChro ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
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社区 版权所有