自定义模块
自定义模块(也就是私人订制),我们要自定义模块,首先就要知道什么是模块
一个函数封装一个功能,比如现在有一个软件,不可能将所有程序都写入一个文件,所以咱们应该分文件,组织结构要好,代码不冗余,所以要分文件,但是分文件,分了5个文件,每个文件里面可能都有相同的功能(函数),怎么办?所以将这些相同的功能封装到一个文件中.
模块就是文件,存放一堆函数,谁用谁拿。怎么拿?
模块是一系列常用功能的集合体,一个py文件就是一个模块
为什么要使用模块?
1、从文件级别组织程序,更方便管理
随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用
2、拿来主义,提升开发效率
同样的原理,我们也可以下载别人写好的模块然后导入到自己的项目中使用,这种拿来主义,可以极大地提升我们的开发效率,避免重复造轮子。
ps:
如果你退出python解释器然后重新进入,那么你之前定义的函数或者变量都将丢失,因此我们通常将程序写到文件中以便永久保存下来,需要时就通过python meet.py方式去执行,此时meet.py被称为脚本script。
1''' 2-*- coding: utf-8 -*- 3@Author : Meet 4@Software: PyCharm 5@File : meet.py 6''' 7print('from the meet.py') 8 9name = 'guoboayuan' 10 11def read1(): 12 print('meet模块:',name) 13 14def read2(): 15 print('meet模块') 16 read1() 17 18def change(): 19 global name 20 name = 'meet'
导入
import 翻译过来是一个导入的意思
1模块可以包含可执行的语句和函数的定义,这些语句的目的是初始化模块,它们只在模块名第一次遇到导入import语句时才执行 2(import语句是可以在程序中的任意位置使用的,且针对同一个模块很import多次,为了防止你重复导入,python的优化手段是: 3第一次导入后就将模块名加载到内存了,后续的import语句仅是对已经加载到内存中的模块对象增加了一次引用,不会重新执行模块内的语句), 4如下 5 6import spam #只在第一次导入时才执行meet.py内代码,此处的显式效果是只打印一次'from the meet.py',当然其他的顶级代码也都被执行了, 只不过没有显示效果.
代码示例:
1import meet 2import meet 3import meet 4import meet 5import meet 6 7执行结果: 只打印一次 8from the meet.py
每个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个模块的名称空间当做全局名称空间,这样我们在编写自己的模块时,
就不用担心我们定义在自己模块中全局变量会在被导入时,与使用者的全局变量冲突
示例:
当前是meet.py
1import meet 2 3name = 'alex' 4print(name) 5print(meet.name) 6''' 7结果: 8from the meet.py 9alex 10guoboayuan 11''' 12import meet 13def read1(): 14 print(666) 15meet.read1() 16''' 17from the meet.py 18meet模块: guoboayuan 19''' 20import meet 21name = '日天' 22meet.change() 23print(name) 24print(meet.name) 25''' 26from the meet.py 27日天 28宝元 29'''
为模块起别名
别名其实就是一个绰号,好处可以将很长的模块名改成很短,方便使用.
1import meet.py as t 2t.read1()
有利于代码的扩展和优化
1#mysql.py 2def sqlparse(): 3 print('from mysql sqlparse') 4#oracle.py 5def sqlparse(): 6 print('from oracle sqlparse') 7 8#test.py 9db_type=input('>>: ') 10if db_type == 'mysql': 11 import mysql as db 12elif db_type == 'oracle': 13 import oracle as db 14 15db.sqlparse()
导入多个模块
1import os,sys,json 这样写可以但是不推荐 2推荐写法 3 4import os 5import sys 6import json
多行导入:易于阅读 易于编辑 易于搜索 易于维护
from ... import ...
from...import...使用
1from meet import name, read1 2print(name) 3read1() 4''' 5执行结果: 6from the meet.py 7guoboayuan 8meet模块: guoboayuan 9'''
from...import... 与import对比
唯一的区别就是:使用from...import...则是将spam中的名字直接导入到当前的名称空间中,所以在当前名称空间中,
直接使用名字就可以了、无需加前缀:meet.
from...import...的方式有好处也有坏处
好处:使用起来方便了
坏处:容易与当前执行文件中的名字冲突
示例演示:
1.执行文件有与模块同名的变量或者函数名,会有覆盖效果。
1name = 'oldboy' 2from meet import name, read1, read2 3print(name) 4''' 5执行结果: 6from the meet.py 7guoboayuan 8''' 9from meet import name, read1, read2 10name = 'oldboy' 11print(name) 12 13''' 14执行结果: 15oldboy 16 17''' 18def read1(): 19 print(666) 20from meet import name, read1, read2 21read1() 22 23''' 24执行结果: 25from the meet.py 26meet模块: guoboayuan 27''' 28from meet import name, read1, read2 29def read1(): 30 print(666) 31read1() 32 33''' 34执行结果: 35from the meet.py 36666 37'''
2.当前位置直接使用read1和read2就好了,执行时,仍然以spam.py文件全局名称空间
1#测试一:导入的函数read1,执行时仍然回到meet.py中寻找全局变量name 2#test.py 3from meet import read1 4name = 'alex' 5read1() 6''' 7执行结果: 8from the meet.py 9meet--> read1 --> guobaoyuan 10''' 11#测试二:导入的函数read2,执行时需要调用read1(),仍然回到meet.py中找read1() 12#test.py 13from meet import read2 14def read1(): 15 print('==========') 16read2() 17 18''' 19执行结果: 20from the meet.py 21meet --> read2 --> 'meet模块' --> read1 -->'guobaoyuan' 22'''
from导入的模式也支持as
1from meet import read1 as read 2read()
from导入的时候,一行导入多个内容
from meet import read1,read2,name
全部导入
1from meet import * 2#from spam import * 把spam中所有的不是以下划线(_)开头的名字都导入到当前位置 3 4#大部分情况下我们的python程序不应该使用这种导入方式,因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差,在交互式环境中导入时没有问题。 5可以使用__all__来控制*(用来发布新版本),在meet.py中新增一行 6__all__=['money','read1'] #这样在另外一个文件中用from spam import *就这能导入列表中规定的两个名字
模块循环导入问题
模块循环/嵌套导入抛出异常的根本原因是由于在python中模块被导入一次之后,就不会重新导入,只会在第一次导入时执行模块内代码
在我们的项目中应该尽量避免出现循环/嵌套导入,如果出现多个模块都需要共享的数据,可以将共享的数据集中存放到某一个地方
在程序出现了循环/嵌套导入后的异常分析、解决方法如下(了解,以后尽量避免)
示范文件内容如下
1#创建一个m1.py 2print('正在导入m1') 3from m2 import y 4 5x='m1' 6 7#创建一个m2.py 8print('正在导入m2') 9from m1 import x 10 11y='m2' 12 13#创建一个run.py 14import m1 15 16#测试一 17执行run.py会抛出异常 18正在导入m1 19正在导入m2 20Traceback (most recent call last): 21 File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/aa.py", line 1, in <module> 22 import m1 23 File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module> 24 from m2 import y 25 File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m2.py", line 2, in <module> 26 from m1 import x 27ImportError: cannot import name 'x' 28 29 30#测试一结果分析 31先执行run.py--->执行import m1,开始导入m1并运行其内部代码--->打印内容"正在导入m1" 32--->执行from m2 import y 开始导入m2并运行其内部代码--->打印内容“正在导入m2”--->执行from m1 import x,由于m1已经被导入过了,所以不会重新导入,所以直接去m1中拿x,然而x此时并没有存在于m1中,所以报错 33 34 35#测试二:执行文件不等于导入文件,比如执行m1.py不等于导入了m1 36直接执行m1.py抛出异常 37正在导入m1 38正在导入m2 39正在导入m1 40Traceback (most recent call last): 41 File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module> 42 from m2 import y 43 File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m2.py", line 2, in <module> 44 from m1 import x 45 File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module> 46 from m2 import y 47ImportError: cannot import name 'y' 48 49 50#测试二分析 51执行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中所以报错 52 53 54 55# 解决方法: 56方法一:导入语句放到最后 57#m1.py 58print('正在导入m1') 59 60x='m1' 61 62from m2 import y 63 64#m2.py 65print('正在导入m2') 66y='m2' 67 68from m1 import x 69 70方法二:导入语句放到函数中 71#m1.py 72print('正在导入m1') 73 74def f1(): 75 from m2 import y 76 print(x,y) 77 78x = 'm1' 79 80# f1() 81 82#m2.py 83print('正在导入m2') 84 85def f2(): 86 from m1 import x 87 print(x,y) 88 89y = 'm2' 90 91#run.py 92import m1 93 94m1.f1()
模块的重载(了解)
考虑到性能的原因,每个模块只被导入一次,放入字典sys.module中,如果你改变了模块的内容,你必须重启程序,python不支持重新加载或卸载之前导入的模块,
有的同学可能会想到直接从sys.module中删除一个模块不就可以卸载了吗,注意了,你删了sys.module中的模块对象仍然可能被其他程序的组件所引用,因而不会被清楚。
特别的对于我们引用了这个模块中的一个类,用这个类产生了很多对象,因而这些对象都有关于这个模块的引用。
py文件的两种功能
1#编写好的一个python文件可以有两种用途: 2 一:脚本,一个文件就是整个程序,用来被执行 3 二:模块,文件中存放着一堆功能,用来被导入使用 4 5 6#python为我们内置了全局变量__name__, 7 当文件被当做脚本执行时:__name__ 等于'__main__' 8 当文件被当做模块导入时:__name__等于模块名 9 10#作用:用来控制.py文件在不同的应用场景下执行不同的逻辑(或者是在模块文件中测试代码) 11if __name__ == '__main__': 12print('from the meet.py') 13 14__all__ = ['name', 'read1', ] 15 16name = 'guobaoyuan' 17 18 19def read1(): 20 print('meet模块:', name) 21 22 23def read2(): 24 print('meet模块') 25 read1() 26 27 28def change(): 29 global name 30 name = '宝元' 31 32 33if __name__ == '__main__': 34 # 在模块文件中测试read1()函数 35 # 此模块被导入时 __name__ 就变成了文件名,if条件不成立 36 # 所以read1不执行 37 read1()
模块的搜索路径
模块的查找顺序是:内存中已经加载的模块->内置模块->sys.path路径中包含的模块
1#模块的查找顺序 21、在第一次导入某个模块时(比如spam),会先检查该模块是否已经被加载到内存中(当前执行文件的名称空间对应的内存),如果有则直接引用 3 ps:python解释器在启动时会自动加载一些模块到内存中,可以使用sys.modules查看 42、如果没有,解释器则会查找同名的内建模块 53、如果还没有找到就从sys.path给出的目录列表中依次寻找spam.py文件。 6 7#需要特别注意的是:我们自定义的模块名不应该与系统内置模块重名。虽然每次都说,但是仍然会有人不停的犯错。 8 9#在初始化后,python程序可以修改sys.path,路径放到前面的优先于标准库被加载。 10>>> import sys 11>>> sys.path.append('/a/b/c/d') 12>>> sys.path.insert(0,'/x/y/z') #排在前的目录,优先被搜索 13注意:搜索时按照sys.path中从左到右的顺序查找,位于前的优先被查找. 14 15#windows下的路径不加r开头,会语法错误 16sys.path.insert(0,r'C:\Users\Administrator\PycharmProjects\a')
编译Python文件(了解)
为了提高加载模块的速度,强调强调强调:提高的是加载速度而绝非运行速度。python解释器会在__pycache__目录中下缓存每个模块编译后的版本,格式为:module.version.pyc。通常会包含python的版本号。例如,在CPython3.3版本下,spam.py模块会被缓存成__pycache__/spam.cpython-33.pyc。这种命名规范保证了编译后的结果多版本共存。
Python检查源文件的修改时间与编译的版本进行对比,如果过期就需要重新编译。这是完全自动的过程。并且编译的模块是平台独立的,所以相同的库可以在不同的架构的系统之间共享,即pyc使一种跨平台的字节码,类似于JAVA火.NET,是由python虚拟机来执行的,但是pyc的内容跟python的版本相关,不同的版本编译后的pyc文件不同,2.5编译的pyc文件不能到3.5上执行,并且pyc文件是可以反编译的,因而它的出现仅仅是用来提升模块的加载速度的,不是用来加密的。
1#提示: 21.模块名区分大小写,foo.py与FOO.py代表的是两个模块 32.在速度上从.pyc文件中读指令来执行不会比从.py文件中读指令执行更快,只有在模块被加载时,.pyc文件才是更快的 43.只有使用import语句是才将文件自动编译为.pyc文件,在命令行或标准输入中指定运行脚本则不会生成这类文件.
time模块
time翻译过来就是时间,有我们其实在之前编程的时候有用到过.
1#常用方法 21.time.sleep(secs) 3(线程)推迟指定的时间运行。单位为秒。 42.time.time() 5获取当前时间戳
在计算中时间共有三种方式:
1.时间戳: 通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量。我们运行“type(time.time())”,返回的是float类型
2.格式化字符串时间: 格式化的时间字符串(Format String): ‘1999-12-06’
1python中时间日期格式化符号: 2%y 两位数的年份表示(00-99) 3%Y 四位数的年份表示(000-9999) 4%m 月份(01-12) 5%d 月内中的一天(0-31) 6%H 24小时制小时数(0-23) 7%I 12小时制小时数(01-12) 8%M 分钟数(00=59) 9%S 秒(00-59) 10%a 本地简化星期名称 11%A 本地完整星期名称 12%b 本地简化的月份名称 13%B 本地完整的月份名称 14%c 本地相应的日期表示和时间表示 15%j 年内的一天(001-366) 16%p 本地A.M.或P.M.的等价符 17%U 一年中的星期数(00-53)星期天为星期的开始 18%w 星期(0-6),星期天为星期的开始 19%W 一年中的星期数(00-53)星期一为星期的开始 20%x 本地相应的日期表示 21%X 本地相应的时间表示 22%Z 当前时区的名称 23%% %号本身
3.结构化时间:元组(struct_time) struct_time元组共有9个元素共九个元素:(年,月,日,时,分,秒,一年中第几周,一年中第几天等)
首先,我们先导入time模块,来认识一下python中表示时间的几种格式:
1#导入时间模块 2>>>import time 3 4#时间戳 5>>>time.time() 61500875844.800804 7 8#时间字符串 9>>>time.strftime("%Y-%m-%d %X") 10'2017-07-24 13:54:37' 11>>>time.strftime("%Y-%m-%d %H-%M-%S") 12'2017-07-24 13-55-04' 13 14#时间元组:localtime将一个时间戳转换为当前时区的struct_time 15time.localtime() 16time.struct_time(tm_year=2017, tm_mon=7, tm_mday=24, 17 tm_hour=13, tm_min=59, tm_sec=37, 18 tm_wday=0, tm_yday=205, tm_isdst=0)
小结:时间戳是计算机能够识别的时间;时间字符串是人能够看懂的时间;元组则是用来操作时间的
时间格式转换:

1#时间戳-->结构化时间 2#time.gmtime(时间戳) #UTC时间,与英国伦敦当地时间一致 3#time.localtime(时间戳) #当地时间。例如我们现在在北京执行这个方法:与UTC时间相差8小时,UTC时间+8小时 = 北京时间 4>>>time.gmtime(1500000000) 5time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=2, tm_min=40, tm_sec=0, tm_wday=4, tm_yday=195, tm_isdst=0) 6>>>time.localtime(1500000000) 7time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=10, tm_min=40, tm_sec=0, tm_wday=4, tm_yday=195, tm_isdst=0) 8 9 10 11#结构化时间-->时间戳 12#time.mktime(结构化时间) 13>>>time_tuple = time.localtime(1500000000) 14>>>time.mktime(time_tuple) 151500000000.0 16 17 18 19#结构化时间-->字符串时间 20#time.strftime("格式定义","结构化时间") 结构化时间参数若不传,则显示当前时间 21>>>time.strftime("%Y-%m-%d %X") 22'2017-07-24 14:55:36' 23>>>time.strftime("%Y-%m-%d",time.localtime(1500000000)) 24'2017-07-14' 25 26 27 28#字符串时间-->结构化时间 29#time.strptime(时间字符串,字符串对应格式) 30>>>time.strptime("2017-03-16","%Y-%m-%d") 31time.struct_time(tm_year=2017, tm_mon=3, tm_mday=16, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=75, tm_isdst=-1) 32>>>time.strptime("07/24/2017","%m/%d/%Y") 33time.struct_time(tm_year=2017, tm_mon=7, tm_mday=24, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=0, tm_yday=205, tm_isdst=-1)

1#结构化时间 --> %a %b %d %H:%M:%S %Y串 2#time.asctime(结构化时间) 如果不传参数,直接返回当前时间的格式化串 3>>>time.asctime(time.localtime(1500000000)) 4'Fri Jul 14 10:40:00 2017' 5>>>time.asctime() 6'Mon Jul 24 15:18:33 2017' 7 8 9 10#时间戳 --> %a %b %d %H:%M:%S %Y串 11#time.ctime(时间戳) 如果不传参数,直接返回当前时间的格式化串 12>>>time.ctime() 13'Mon Jul 24 15:19:07 2017' 14>>>time.ctime(1500000000) 15'Fri Jul 14 10:40:00 2017'
我们看完了time在来看一个Python处理日期和时间的标准库
datetime
获取当前日期和时间
1from datetime import datetime 2 3print(datetime.now()) 4 5''' 6结果:2018-12-04 21:07:48.734886 7'''
注意:datetime是模块,datetime模块还包含一个datetime的类,通过from datetime import datetime导入的才是datetime这个类。
如果仅导入import datetime,则必须引用全名datetime.datetime。
datetime.now()返回当前日期和时间,其类型是datetime。
获取指定日期和时间
要指定某个日期和时间,我们直接用参数构造一个datetime:
1from datetime import datetime 2 3dt = datetime(2018,5,20,13,14) 4print(dt) 5 6''' 7结果:2018-05-20 13:14:00 8'''
datetime转换为timestamp(时间戳)
1from datetime import datetime 2 3dt = datetime.now() 4new_timestamp = dt.timestamp() 5print(new_timestamp) 6 7''' 8结果:1543931750.415896 9'''
timestamp转换为datetime
1import time 2from datetime import datetime 3 4new_timestamp = time.time() 5print(datetime.fromtimestamp(new_timestamp))
str转换为datetime
很多时候,用户输入的日期和时间是字符串,要处理日期和时间,首先必须把str转换为datetime。转换方法是通过datetime.strptime()实现,需要一个日期和时间的格式化字符串:
1from datetime import datetime 2 3t = datetime.strptime('2018-4-1 00:00','%Y-%m-%d %H:%M') 4print(t) 5''' 6结果: 2018-04-01 00:00:00 7'''
datetime转换为str
如果已经有了datetime对象,要把它格式化为字符串显示给用户,就需要转换为str,转换方法是通过strftime()实现的,同样需要一个日期和时间的格式化字符串:
1from datetime import datetime 2now = datetime.now() 3print(now.strftime('%a, %b %d %H:%M')) 4Mon, May 05 16:28
datetime加减
对日期和时间进行加减实际上就是把datetime往后或往前计算,得到新的datetime。加减可以直接用+和-运算符,不过需要导入timedelta这个类:
1from datetime import datetime, timedelta 2now = datetime.now() 3print(now) 4now1 = now + timedelta(hours=10) 5print(now1) 6now2 = now - timedelta(days=1) 7print(now2) 8now3 = now + timedelta(days=2, hours=12) 9print(now3)
可见,使用timedelta你可以很容易地算出前几天和后几天的时刻。
小结注意
datetime表示的时间需要时区信息才能确定一个特定的时间,否则只能视为本地时间。