python模块是一个可共享的程序_python基础-模块
一 模块介绍
#一个python文件有两种用途#1、被当成程序运行#2、被当做模块导入
在Python中,一个py文件就是一个模块,文件名为xxx.py模块名则是xxx,导入模块可以引用模块中已经写好的功能。如果把开发程序比喻成制造一台电脑,编写模块就像是在制造电脑的零部件,准备好零部件后,剩下的工作就是按照逻辑把它们组装到一起。
将程序模块化会使得程序的组织结构清晰,维护起来更加方便。比起直接开发一个完整的程序,单独开发一个小的模块也会更加简单,并且程序中的模块与电脑中的零部件稍微不同的是:程序中的模块可以被重复使用。所以总结下来,使用模块既保证了代码的重用性,又增强了程序的结构性和可维护性。另外除了自定义模块外,我们还可以导入使用内置或第三方模块提供的现成功能,这种“拿来主义”极大地提高了程序员的开发效率。
模块就是一系列功能的集合体,分为三大类
I:内置的模块
II:第三方的模块
III:自定义的模块
一个python文件本身就一个模块,文件名m.py,模块名叫m
ps:模块有四种形式
1 使用python编写的.py文件
2 已被编译为共享库或DLL的C或C++扩展
3 把一系列模块组织到一起的文件夹(注:文件夹下有一个__init__.py文件,该文件夹称之为包)
4 使用C编写并链接到python解释器的内置模块
I:内置与第三的模块拿来就用,无需定义,这种拿来主义,可以极大地提升自己的开发效率
II:自定义的模块
可以将程序的各部分功能提取出来放到一模块中为大家共享使用
好处是减少了代码冗余,程序组织结构更加清晰
二 模块的使用
2.1 import语句
y=333z=444
importfoo#1、首次导入模块会发生3件事#1、执行foo.py#2、产生foo.py的名称空间,将foo.py运行过程中产生的名字都丢到foo的名称空间中#3、在当前文件中产生的有一个名字foo,该名字指向2中产生的名称空间
#之后的导入,都是直接引用首次导入产生的foo.py名称空间,不会重复执行代码#import foo#import foo#import foo#import foo
有如下示范文件
#文件名:foo.py
x=1
defget():print(x)defchange():globalx
x=0classFoo:deffunc(self):print('from the func')
要想在另外一个py文件中引用foo.py中的功能,需要使用import foo,首次导入模块会做三件事:
1、执行源文件代码
2、产生一个新的名称空间用于存放源文件执行过程中产生的名字
3、在当前执行文件所在的名称空间中得到一个名字foo,该名字指向新创建的模块名称空间,若要引用模块名称空间中的名字,需要加上该前缀,如下
import foo #导入模块foo
a=foo.x #引用模块foo中变量x的值赋值给当前名称空间中的名字a
foo.get() #调用模块foo的get函数
foo.change() #调用模块foo中的change函数
obj=foo.Foo() #使用模块foo的类Foo来实例化,进一步可以执行obj.func()
加上foo.作为前缀就相当于指名道姓地说明要引用foo名称空间中的名字,所以肯定不会与当前执行文件所在名称空间中的名字相冲突,并且若当前执行文件的名称空间中存在x,执行foo.get()或foo.change()操作的都是源文件中的全局变量x。
需要强调一点是,第一次导入模块已经将其加载到内存空间了,之后的重复导入会直接引用内存中已存在的模块,不会重复执行文件,通过import sys,打印sys.modules的值可以看到内存中已经加载的模块名。
#强调1:模块名.名字,是指名道姓地问某一个模块要名字对应的值,不会与当前名称空间中的名字发生冲突#x=1111111111111#print(x)#print(foo.x)
#强调2:无论是查看还是修改操作的都是模块本身,与调用位置无关#import foo#
#x=3333333333## foo.get()#
#foo.change()#print(x)#
#print(foo.x)#foo.get()
提示:
#1、在Python中模块也属于第一类对象,可以进行赋值、以数据形式传递以及作为容器类型的元素等操作。
#2、模块名应该遵循小写形式,标准库从python2过渡到python3做出了很多这类调整,比如ConfigParser、Queue、SocketServer全更新为纯小写形式。
用import语句导入多个模块,可以写多行import语句
importmodule1importmodule2
...import moduleN
还可以在一行导入,用逗号分隔开不同的模块
import module1,module2,...,moduleN
但其实第一种形式更为规范,可读性更强,推荐使用,而且我们导入的模块中可能包含有python内置的模块、第三方的模块、自定义的模块,为了便于明显地区分它们,我们通常在文件的开头导入模块,并且分类导入,一类模块的导入与另外一类的导入用空行隔开,不同类别的导入顺序如下:
#1. python内置模块#2. 第三方模块#3. 程序员自定义模块
当然,我们也可以在函数内导入模块,对比在文件开头导入模块属于全局作用域,在函数内导入的模块则属于局部的作用域。
#4、导入模块的规范#I. python内置模块#II. 第三方模块#III. 程序员自定义模块
#import time#import sys#
#import 第三方1#import 第三方2#
#import 自定义模块1#import 自定义模块2#import 自定义模块3
#5、模块是第一类对象
importfoo#6、自定义模块的命名应该采用纯小写+下划线的风格#7、可以在函数内导入模块
deffunc():import foo
2.2 from-import 语句
#impot导入模块在使用时必须加前缀"模块."#优点:肯定不会与当前名称空间中的名字冲突#缺点:加前缀显得麻烦
#from ... import ...导入也发生了三件事#1、产一个模块的名称空间#2、运行foo.py将运行过程中产生的名字都丢到模块的名称空间去#3、在当前名称空间拿到一个名字,该名字与模块名称空间中的某一个内存地址#from foo import x # x=模块foo中值0的内存地址#from foo import get#from foo import change
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语句支持from foo import 语法,代表将foo中所有的名字都导入到当前位置
from foo import * #把foo中所有的名字都导入到当前执行文件的名称空间中,在当前位置直接可以使用这些名字
a=x
get()
change()
obj=Foo()
如果我们需要引用模块中的名字过多的话,可以采用上述的导入形式来达到节省代码量的效果,但是需要强调的一点是:只能在模块最顶层使用的方式导入,在函数内则非法,并且的方式会带来一种副作用,即我们无法搞清楚究竟从源文件中导入了哪些名字到当前位置,这极有可能与当前位置的名字产生冲突。模块的编写者可以在自己的文件中定义__all__变量用来控制*代表的意思
#foo.py
__all__=['x','get'] #该列表中所有的元素必须是字符串类型,每个元素对应foo.py中的一个名字
x=1
defget():print(x)defchange():globalx
x=0classFoo:deffunc(self):print('from the func')
这样我们在另外一个文件中使用*导入时,就只能导入__all__定义的名字了
from foo import * #此时的*只代表x和get
x#可用
get() #可用
change() #不可用
Foo() #不可用
#from...impot...导入模块在使用时不用加前缀#优点:代码更精简#缺点:容易与当前名称空间混淆#from foo import x # x=模块foo中值1的内存地址#x=1111
#一行导入多个名字(不推荐)#from foo import x,get,change
2.3 其他导入语法(as)
我们还可以在当前位置为导入的模块起一个别名
import foo as f #为导入的模块foo在当前位置起别名f,以后再使用时就用这个别名f
f.x
f.get()
还可以为导入的一个名字起别名
from foo importget as get_x
get_x()
通常在被导入的名字过长时采用起别名的方式来精简代码,另外为被导入的名字起别名可以很好地避免与当前名字发生冲突,还有很重要的一点就是:可以保持调用方式的一致性,例如我们有两个模块json和pickle同时实现了load方法,作用是从一个打开的文件中解析出结构化的数据,但解析的格式不同,可以用下述代码有选择性地加载不同的模块
if data_format == 'json':import json as serialize #如果数据格式是json,那么导入json模块并命名为serialize
elif data_format == 'pickle':import pickle as serialize #如果数据格式是pickle,那么导入pickle模块并命名为serialize
data=serialize.load(fn) #最终调用的方式是一致的
2.4 循环导入问题
循环导入问题指的是在一个模块加载/导入的过程中导入另外一个模块,而在另外一个模块中又返回来导入第一个模块中的名字,由于第一个模块尚未加载完毕,所以引用失败、抛出异常,究其根源就是在python中,同一个模块只会在第一次导入时执行其内部代码,再次导入该模块时,即便是该模块尚未完全加载完毕也不会去重复执行内部代码
我们以下述文件为例,来详细分析循环/嵌套导入出现异常的原因以及解决的方案
m1.py
print('正在导入m1')from m2 importy
x='m1'
m2.py
print('正在导入m2')from m1 importx
y='m2'
run.py
import m1
测试一
#1、执行run.py会抛出异常
正在导入m1
正在导入m2
Traceback (most recent call last):
File"/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/aa.py", line 1, in
importm1
File"/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in
from m2 importy
File"/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m2.py", line 2, in
from m1 importx
ImportError: cannotimport name 'x'
#2、分析
先执行run.py--->执行import m1,开始导入m1并运行其内部代码--->打印内容"正在导入m1"
--->执行from m2 import y 开始导入m2并运行其内部代码--->打印内容“正在导入m2”--->执行from m1 import x,由于m1已经被导入过了,所以不会重新导入,所以直接去m1中拿x,然而x此时并没有存在于m1中,所以报错
测试二
#1、执行文件不等于导入文件,比如执行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
from m2 importy
File"/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m2.py", line 2, in
from m1 importx
File"/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in
from m2 importy
ImportError: cannotimport name 'y'
#2、分析
执行m1.py,打印“正在导入m1”,执行from m2 import y ,导入m2进而执行m2.py内部代码--->打印"正在导入m2",执行from m1 import x,此时m1是第一次被导入,执行m1.py并不等于导入了m1,
于是开始导入m1并执行其内部代码--->打印"正在导入m1",执行from m1 import y,由于m1已经被导入过了,所以无需继续导入而直接问m2要y,然而y此时并没有存在于m2中所以报错
解决方案
#方案一:导入语句放到最后,保证在导入时,所有名字都已经加载过#文件:m1.py
print('正在导入m1')
x='m1'
from m2 importy#文件:m2.py
print('正在导入m2')
y='m2'
from m1 importx#文件:run.py内容如下,执行该文件,可以正常使用
importm1print(m1.x)print(m1.y)#方案二:导入语句放到函数中,只有在调用函数时才会执行其内部代码#文件:m1.py
print('正在导入m1')deff1():from m2 importyprint(x,y)
x= 'm1'
#文件:m2.py
print('正在导入m2')deff2():from m1 importxprint(x,y)
y= 'm2'
#文件:run.py内容如下,执行该文件,可以正常使用
importm1
m1.f1()
注意:循环导入问题大多数情况是因为程序设计失误导致,上述解决方案也只是在烂设计之上的无奈之举,在我们的程序中应该尽量避免出现循环/嵌套导入,如果多个模块确实都需要共享某些数据,可以将共享的数据集中存放到某一个地方,然后进行导入
2.5 搜索模块的路径与优先级
模块其实分为四个通用类别,分别是:
1、使用纯Python代码编写的py文件
2、包含一系列模块的包
3、使用C编写并链接到Python解释器中的内置模块
4、使用C或C++编译的扩展模块
在导入一个模块时,如果该模块已加载到内存中,则直接引用,否则会优先查找内置模块,然后按照从左到右的顺序依次检索sys.path中定义的路径,直到找模块对应的文件为止,否则抛出异常。sys.path也被称为模块的搜索路径,它是一个列表类型
>>>sys.path
['','/Library/Frameworks/Python.framework/Versions/3.5/lib/python35.zip','/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5',
...,'/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages'
列表中的每个元素其实都可以当作一个目录来看:在列表中会发现有.zip或.egg结尾的文件,二者是不同形式的压缩文件,事实上Python确实支持从一个压缩文件中导入模块,我们也只需要把它们都当成目录去看即可。
sys.path中的第一个路径通常为空,代表执行文件所在的路径,所以在被导入模块与执行文件在同一目录下时肯定是可以正常导入的,而针对被导入的模块与执行文件在不同路径下的情况,为了确保模块对应的源文件仍可以被找到,需要将源文件foo.py所在的路径添加到sys.path中,假设foo.py所在的路径为/pythoner/projects/
importsys
sys.path.append(r'/pythoner/projects/') #也可以使用sys.path.insert(……)
import foo #无论foo.py在何处,我们都可以导入它了
#无论是import还是from...import在导入模块时都涉及到查找问题#优先级:#1、内存(内置模块)#2、硬盘:按照sys.path中存放的文件的顺序依次查找要导入的模块
#import sys#值为一个列表,存放了一系列的对文件夹#其中第一个文件夹是当前执行文件所在的文件夹#print(sys.path)
#import foo # 内存中已经有foo了#foo.say()#
#import time#time.sleep(10)#
#import foo#foo.say()
#了解:sys.modules查看已经加载到内存中的模块
importsys#import foo # foo=模块的内存地址#del foo
#def func():#import foo # foo=模块的内存地址#
#func()#
## print('foo' in sys.modules)#print(sys.modules)
2.6 区分py文件的两种用途
一个Python文件有两种用途,一种被当主程序/脚本执行,另一种被当模块导入,为了区别同一个文件的不同用途,每个py文件都内置了__name__变量,该变量在py文件被当做脚本执行时赋值为“__main__”,在py文件被当做模块导入时赋值为模块名
作为模块foo.py的开发者,可以在文件末尾基于__name__在不同应用场景下值的不同来控制文件执行不同的逻辑
#foo.py
...if __name__ == '__main__':
foo.py被当做脚本执行时运行的代码else:
foo.py被当做模块导入时运行的代码
通常我们会在if的子代码块中编写针对模块功能的测试代码,这样foo.py在被当做脚本运行时,就会执行测试代码,而被当做模块导入时则不用执行测试代码。
2.7 编写一个规范的模块
我们在编写py文件时,需要时刻提醒自己,该文件既是给自己用的,也有可能会被其他人使用,因而代码的可读性与易维护性显得十分重要,为此我们在编写一个模块时最好按照统一的规范去编写,如下
#!/usr/bin/env python #通常只在类unix环境有效,作用是可以使用脚本名来执行,而无需直接调用解释器。
"The module is used to..." #模块的文档描述
import sys #导入模块
x=1 #定义全局变量,如果非必须,则最好使用局部变量,这样可以提高代码的易维护性,并且可以节省内存提高性能
class Foo: #定义类,并写好类的注释
'Class Foo is used to...'
pass
def test(): #定义函数,并写好函数的注释
'Function test is used to…'
pass
if __name__ == '__main__': #主程序
test() #在被当做脚本执行时,执行此处的代码
python模块是一个可共享的程序_python基础-模块相关推荐
- python写采集程序_用python写的一个wordpress的采集程序
在学习python的过程中,经过不断的尝试及努力,终于完成了第一个像样的python程序,虽然还有很多需要优化的地方,但是目前基本上实现了我所要求的功能,先贴一下程序代码: 具体代码如下: #! /u ...
- Python脚本做一个淘宝秒杀程序!
Python脚本做一个淘宝秒杀程序! 先上代码 from selenium import webdriver import datetime import time import os import ...
- python写机器人程序_用Python写的一个多线程机器人聊天程序
本人是从事php开发的, 近来想通过php实现即时通讯(兼容windows).后来发现实现起来特别麻烦, 就想到python.听说这家伙在什么地方都能发挥作用.所以想用python来做通讯模块...所 ...
- 我用Python写的一个操控玩具车的程序
要用Python写一个玩具遥控车的操控程序,需要考虑以下几个方面: 1.硬件连接:首先,需要连接遥控车的硬件设备(例如,通过蓝牙或红外线连接)并建立通信,以便从程序中控制车辆. 2. 控制指令:其次, ...
- python执行到input后执行下一程序_Python基础知识储备,如何开关一个while循环
一.什么是循环 循环语句就是在某种条件下,一遍一遍重复的执行某个动作. 如:从1加到100,重复执行加法的动作,就需要用到循环. 二.循环的三要素 虽然循环是反复的执行某个动作,但是循环也会停止的,没 ...
- python设计一个验证用户密码程序_Python如何实现一个登陆验证(文件验证)
本篇文章给大家带来的内容是关于Python如何实现一个登陆验证(文件验证),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 代码主要功能: 利用Python实现简单的登陆验证,代码主要 ...
- 【python】制作一个点单小程序!
周末总是在吃的方面,及其纠结,今天来制作一个点单小程序,加入自己喜欢吃的东西,来慢慢挑选,让每个周末快乐无限! 一.安装环境 python 3.7.8 QT xlrd.xlwt库使用pip接口进行安装 ...
- python最常用的编程方式是什么_python常用模块和对象编程
1.摘要 常用模块 对象编程 领域模型 2.常用模块 2.1shutil模块 2.1.1 shutil.copyfileobj(fsrc, fdst[, length]) 1 f = open(&qu ...
- python 用if判断一个数是不是整数_Python基础教程07-函数和模块的使用
在讲解本章节的内容之前,我们先来研究一道数学题,请说出下面的方程有多少组正整数解. x1+x2+x3+x4=8 事实上,上面的问题等同于将8个苹果分成四组每组至少一个苹果有多少种方案.想到这一点问题的 ...
最新文章
- org.codehaus.plexus.archiver.jar.Manifest.write(java.io.PrintWriter)
- POJ3762 时间段用k次
- Linux:-bash: ***: command not found
- JDK JRE 的作用与区别【转】
- 微型计算机计算机钢琴,微型计算机原理及接口技术钢琴课程设计.doc
- C#中的DES加解密方法
- 金融数据分析与挖掘实战练习2.5-2.9
- qq浏览器插件_惊艳与吐槽:QQ浏览器升级到 Chromium70 内核以后
- mysql分析函数的实现
- android图片混淆还原,图片混淆还原1.2版本
- js判断IE浏览器及版本
- SQLServer 分组查询相邻两条记录的时间差
- 群晖NAS详细教程 DSM6.1.7版本(亲测有效)传统BIOS
- 改变TMQQ2009版消息提示音
- 【坦白说】如何确定对方身份?
- (伪)原创,采集工具应用
- CSAPP AttackLab解题记录
- 降低漏洞风险的六大法宝!
- 字符串要求字母、数字、特殊字符,至少有两项。正则表达式不好写,可以用这个方法实现。
- ChatGPT 如何获取API Key
热门文章
- (万里开源)greatdb mysql 8.0以上版本创建用户并授权远程连接
- android中的mvp模式怎么定义,详解MVP模式在Android开发中的应用
- android fragment学习6--FragmentTabHost底部布局
- 如何获取android源码
- 基于JAVA+SpringMVC+MYSQL的营业厅终端销售系统
- java封装数组_Java封装数组之动态数组实现方法详解
- 题解P3951【小凯的疑惑】
- AnnotationTransactionAttributeSource is only available on Java 1.5 and higher
- 在JavaScript里 (a ==1 a== 2 a==3) 有可能是 true 吗?
- Innobackupx工具命令简单解析