Python学习笔记 - day9 - 模块与包
模块与包
一个模块就是一个包含了Python定义和声明的文件,文件名就是模块名加上.py的后缀,导入一个py文件,解释器解释该py文件,导入一个包,解释器解释该包下的 __init__.py 文件,所以如果我们要创建一个包文件,那么该文件下必须要有__init__.py文件。
包
从目录级别来组织模块的,本质上也是被导入的,目录下有 __init__ 文件的都可以理解为包,在import时,其实导入的就是包下的 __init__.py 文件。
- 无论是import形式,还是from...import形似,凡是在导入语句中(而不是使用时)遇到带点的,就是在导入包
- 包是目录级的(文件夹),文件夹是用来组织py文件的。(包的本质就是一个包含 __init__.py 文件的目录)
- import导入文件时,产生的名称空间来源于文件,import包,产生的命名空间同样来源于文件,既包下面的__init__.py,导入包的本质就是导入该文件
包的import *
这个* 其实读取的就是 __init__ 文件里面的所有函数,所以一般不要用*
通过在父包内的__init__文件中利用相对路径进行导入子包的模块或者方法,来达到通过(父包.方法)来调用,屏蔽了不同子包路径过长的问题
模块
要想使用模块需要先行倒入,那么根据导入方式的不同主要有两种方式:import 和 from。
import导入
导入操作:
1)import导入模块就会执行要导入的模块的所有代码
2)产生新的名称空间
3)拿到一个模块名,指向模块中的代码
语法:
import module as alias # 导入时并定义别名 import module,module1,module2 # 一次导入多个模块(不建议)
调用方式:使用包名.函数名来执行导入的函数
import time print(time.strftime('%Y-%m-%d %H:%M:%S')) # 导入time模块 # 使用 time.strftime()来调用strftime()函数
注意:使用import方式会导入包中的所有函数。
from导入
导入操作:
1)会执行要导入的模块的所有代码,调用时只通过func就可以调用
2)如果func和本文件中存在同名func,那么就会覆盖
语法:
from module import func # 调用时可以直接使用func,而不必使用module.func来调用 from module import * # 导入所有函数
PS:在被导入模块的__init__.py文件中定义__all__ = ['module','module1'],这样在导入其时,用*,只能导入 __all__ 定义的模块
调用方式:直接使用函数名进行调用。
from time import strftime print(strftime('%Y-%m-%d %H:%M:%S'))
PS:func来自哪个文件,那么func内部调用的变量,以其所在文件中的环境变量为准
模块中的特殊关键字及路径
模块文件中有一些Python提供的特殊关键字,例如
__file__ :打印当前文件的文件名
__name__ :显示当前模块是否被导入,显示:__main__ 表示没有被导入,显示:模块名, 表示被此模块被导入。
PS:如果我们写的Python文件中的某个函数或者类会被其他人调用,使用if __name__ == '__main__' ,后面写主逻辑,这样在别的地方导入,就不会执行下面的代码段,而直接运行文件时,就会执行。
模块路径
顾名思义,导入模块是需要先找到该模块,和在linux中执行命令是相同的,在Python中导入模块的搜索顺序:
- 先从内存中寻找
- 然后在内置模块中寻找
- 然后在sys.path中查找
关于sys.path,类似于shell中的PATH,其是list类型,每个元素为路径的字符串形式,列表的第一个路径默认为当前路径,使用sys.path.append('dirpath'),就可以把路径加入到sys.path中
Python内置模块
Python中内置了很多模块,可以提供很多实用的功能,下面将会列举部分模块。
datetime模块
提供时间功能的模块其实不止datetime,Python内置的还有一个time模块,time模块可以完成格式化输出,但是用起来没有datetime模块方便,但在下面场景下会用到time模块
import time for i in range(1,10): print(i) time.sleep(1) # 类似于Linux中的sleep命令。
获取当前日期和时间
datetime.now() 格式化输出一个datetime.datetime对象,打印则显示当前时间
from datetime import datetime print(type(datetime.now())) print(datetime.now())
注意到datetime
是模块,datetime
模块还包含一个datetime
类,通过from datetime import datetime
导入的才是datetime
这个类,如果仅导入import datetime
,则必须引用全名datetime.datetime.now()
。
获取指定的日期与时间
通过是datetime对象实例化的时候,传入指定的时间参数,来获取指定的日期与时间的时间格式字符。
from datetime import datetime timer = datetime(2017,11,9,11,20) print(timer)
注意:时间日期如果是个位数,不能手动补0,比如9可以,09就不行。
获取时间戳
在计算机中,时间实际上是用数字表示的。我们把1970年1月1日 00:00:00 UTC+00:00时区的时刻称为epoch time(元时间),记为0
(1970年以前的时间timestamp为负数),当前时间就是相对于epoch time的秒数,称为timestamp(时间戳)。
- 所以针对于元时间来说:
timestamp = 0 = 1970-1-1 00:00:00 UTC+0:00
- 对应的北京时间是:
timestamp = 0 = 1970-1-1 08:00:00 UTC+8:00(因为北京属于东八区)
可见timestamp的值与时区毫无关系,因为timestamp一旦确定,其UTC时间就确定了,转换到任意时区的时间也是完全确定的,这就是为什么计算机存储的当前时间是以timestamp表示的,因为全球各地的计算机在任意时刻的timestamp都是完全相同的(假定时间已校准)。
from datetime import datetime print(datetime.now().timestamp()) # 注意 timestamp(),方法可以作用在datetime.datetime对象上。 (datetime(2017,11,9,11,50)因为返回的是datetime.datetime对象,所以也可以使用timestamp()来转换)
时间戳转换为格式化时间
要把timestamp转换为datetime
,使用datetime
提供的fromtimestamp()
方法:
from datetime import datetime t = datetime.now().timestamp() print(t) t2 = datetime.fromtimestamp(t) print(t2)
注意到timestamp是一个浮点数,它没有时区的概念,而datetime是有时区的。上述转换是在timestamp和本地时间做转换。如果我们计算机本地时间为 UTC +8,那么获得的时间就是基于本地UTC时间的。
获取前几个小时或后几天
获取前几个小时或后几天,其实是对日期和时间进行加减,得到新的datetime。加减可以直接用+
和-
运算符,不过需要导入timedelta
这个类:
from datetime import datetime,timedelta now = datetime.now() timer = now - timedelta(hours=10) print(timer) # timedelta 默认为天,其他参数还有 days,hours,minutes等。 # 传递的参数可以为负数
datetime对象转换为字符串时间
如果已经有了datetime对象,要把它格式化为一个特定的字符串显示给用户,就需要转换为str,转换方法是通过strftime()
实现的,同样需要一个日期和时间的格式化字符串:
from datetime import datetime str_time = datetime.now().strftime('%Y-%M-%d %H:%M:%S') print(str_time)
字符串时间转换为datetime对象
很多时候,用户输入的日期和时间是字符串,要处理日期和时间,首先必须把str转换为datetime。转换方法是通过datetime.strptime()
实现,需要一个日期和时间的格式化字符串:‘
from datetime import datetime str_time = '2017-11-09 17:15:48' date_time = datetime.strptime(str_time,'%Y-%m-%d %H:%M:%S') print(date_time)
logging模块
logging模块用于日志的打印记录等,是一个健壮的Python程序所必须具有的模块。
logging模块支持的日志级别主要有五种:DEBUG,INFO,WARNING,ERROR,CRITICAL。优先级为:CRITICAL > ERROR > WARNING > INFO > DEBUG 。 默认的级别为WARNING。Python只会打印比当前级别高的日志,比如我同时刷出了INFO和ERROR级别的日志,如果我的日志级别设置的为WAIRNING,那么只会打印ERROR的日志。
基本使用
引入logging模块,通过logging.级别来输出日志信息
import logging logging.debug('debug level message') logging.info('info level message') logging.warning('warning level message') logging.error('error level message') logging.critical('crtical level message')
注意:由于默认级别为WARNING,所以上面的例子只会打印WARNING、ERROR、CRITICAL输出的信息
定义日志格式并输出到文件
logging提供了基础的日志模版,可以定义日志的输出格式,以及输出位置
import logging logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', datefmt='%Y-%m-%d %H:%M:%S', filename='log.log', filemode='w') logging.debug('debug level message') logging.info('info level message') logging.warning('warning level message') logging.error('error level message') logging.critical('crtical level message')
basicConfig支持的参数含义为:
- level:为日志级别,可选参数有logging.DEBUG,logging.INFO,logging.WARNING,logging.ERROR,logging.CRITICAL,分别对应logging的五种日志级别。
- filename: 表示日志文件的名称及路径。
- filemode: 表示日志文件的打开模式,默认不指定的情况下为a,可以改为w。
- datefmt: 对时间进行格式定制,和时间的格式化字符是相同的。
- format: 对日志格式进行定义。
1 %(name)s # Logger的名字 2 %(levelno)s # 数字形式的日志级别 3 %(levelname)s # 文本形式的日志级别 4 %(pathname)s # 调用日志输出函数的模块的完整路径名,可能没有 5 %(filename)s # 调用日志输出函数的模块的文件名 6 %(module)s # 调用日志输出函数的模块名 7 %(funcName)s # 调用日志输出函数的函数名 8 %(lineno)d # 调用日志输出函数的语句所在的代码行 9 %(created)f # 当前时间,用UNIX标准的表示时间的浮 点数表示 10 %(relativeCreated)d # 输出日志信息时的,自Logger创建以 来的毫秒数 11 %(asctime)s # 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒 12 %(thread)d # 线程ID。可能没有 13 %(threadName)s # 线程名。可能没有 14 %(process)d # 进程ID。可能没有 15 %(message)s # 用户输出的消息
当然logging还有更多灵活的模块:logger,handler,filter,formatter。参考:http://www.jb51.net/article/88449.htm
多模块利用logging记录日志
我们可以把logging写到一个模块中,然后在需要记录的地方,导入模块,记录信息即可。
# writelog模块: import logging logging.basicConfig(level=logging.WARNING, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', datefmt='%Y-%m-%d %H:%M:%S', filename='log.log', filemode='a') # 需要记录日志的py文件 import writelog writelog.logging.debug('debug level message') writelog.logging.info('info level message') writelog.logging.warning('warning level message') writelog.logging.error('error level message') writelog.logging.critical('crtical level message')
OS模块
OS模块简单的来说它是一个Python的系统编程的操作模块,可以处理文件和目录这些我们日常手动需要做的操作,和所在的操作系统版本无关。查看OS模块的帮助文档
>>> import os # 导入os模块 >>> help(os) # 查看os模块帮助文档,里面详细的模块相关函数和使用方法
常用功能介绍
os模块中提供了特别多的和系统相关的操作功能,这里列举一些常用功能
os.getcwd() # 获取当前路径 os.chdir() # 切换当前目录,当路径中存在\的时候,由于是转意的意思,那么就需要对\进行转意,那么路径就是c:\\User,或者在目录前面加r,表示后面的字符串不进行解释 os.curdir() # 获取当前目录名 os.pardir() # 获取上级目录名 os.mkdir('dir') # 创建目录,注意只能创建一级目录 os.makedirs('dir_path') # 创建多级目录 os.rmdir('dir') # 删除一个目录 os.removedir('dir_path') # 删除多级目录(目录为空的话) os.listdir('dir') # 显示目录下的所有文件,默认为当前目录,返回的结果为list os.remove('file') # 删除一个文件 os.rename('old_name','new_name') # 修改文件名称 os.stat('file/dir') # 获取文件/目录的stat信息 os.sep # 返回当前操作系统的路径分隔符 # Windows下:\\ , Linux下:/ os.linesep # 返回当前操作系统的换行符 # Windows下:\r\n ,Linux下:\n os.pathsep # 返回当前操作系统环境变量分隔符 # Windows下:; , Linux下: : os.name # 返回当前系统的类型 # nt 表示Windows, posix表示Linux os.system('Commmand') # 执行命令 os.environ # 获取系统环境变量,使用字典存储 os.path.abspath('dir/file') # 获取dir/file的绝对路径 os.path.split('path') # 把路径分割为目录和文件名组成的元组格式,不管path是否存在 os.dirname('path') # 获取文件的父目录名称,不管path是否存在 os.basename('path') # 获取文件的名称,不管path是否存在 os.path.exists('path') # 判断path是否存在,return bool os.path.isabs('path') # 判断path是否是从根开始,return bool # Linux下:从/开始 Windows下从C,D,E盘开始 os.path.isfile('path') # 判断path是否是一个文件 os.path.isdir('path') # 判断path是否是一个目录 os.path.join('path1','path2') # 把path1和path2进行组合 os.path.getatime('path') # 获取文件的atime时间,返回时间戳 os.path.getmtime('path') # 获取文件的mtime时间,返回时间戳 os.path.getsize('name') # 获取文件的大小
commands模块
commands模块专门用于调用Linux shell命令,并返回状态和结果,和os的popen功能类似,该模块一共有三个函数:getoutput,getstatus,getstatusoutput
commands.getoutput('Linux shell command') # 执行Linux shell命令,并获取命令执行的结果 commands.getstatus('filename') # 传递filename,执行 'ls -ld filename' 的命令。 commands.getstatusoutput('Linux shell command') # 执行Linux shell命令,返回的是一个元组.。(命令返回code,结果)
实例:
>>> import commands >>> commands.getoutput('who') 'DahlHin console Nov 9 09:38 \nDahlHin ttys000 Nov 10 10:30 ' >>> commands.getstatus('.pip') 'drwxr-xr-x 3 DahlHin staff 96 10 21 21:44 .pip' >>> commands.getstatusoutput('asasas') (32512, 'sh: asasas: command not found') >>>
注意:commands模块在Python 3.x中已经被subprocess替代!
sys模块
sys模块负责程序与python解释器的交互,提供了一系列的函数和变量,用于操控python的运行时环境。
常用函数:
sys.path # 列出当前Python环境变量,也就是导入时模块的搜索路径 sys.argv # 类似于shell脚本的$1 $2 ,用于传递命令行参数,使用列表进行传递 sys.stdout.write(‘#’) # 在屏幕上输出,但是不换行,配合for循环,比较适合用来做进度条等 sys.stdout.flush() # 配合sys.stdout.write使用,把每次输出的信息,强制刷新到屏幕上,达到动态的效果
1 import sys,time 2 for i in range(20): 3 sys.stdout.write('=') 4 sys.stdout.flush() 5 time.sleep(1)
注意:我们可以把自己写的一些模块加到Python环境变量中去,由于sys.path是list类型,所以只需要append('path'),就可以完成添加。
1 sys.modules.keys() # 返回所有已经导入的模块列表 2 sys.exc_info() # 获取当前正在处理的异常类,exc_type、exc_value、exc_traceback当前处理的异常详细信息 3 sys.exit(n) # 退出程序,正常退出时exit(0) 4 sys.hexversion # 获取Python解释程序的版本值,16进制格式如:0x020403F0 5 sys.version # 获取Python解释程序的版本信息 6 sys.maxint # 最大的Int值 7 sys.maxunicode # 最大的Unicode值 8 sys.modules # 返回系统导入的模块字段,key是模块名,value是模块 9 sys.platform # 返回操作系统平台名称 10 sys.stdout # 标准输出 11 sys.stdin # 标准输入 12 sys.stderr # 错误输出 13 sys.exc_clear() # 用来清除当前线程所出现的当前的或最近的错误信息 14 sys.exec_prefix # 返回平台独立的python文件安装的位置 15 sys.byteorder # 本地字节规则的指示器,big-endian平台的值是'big',little-endian平台的值是'little' 16 sys.copyright # 记录python版权相关的东西 17 sys.api_version # 解释器的C的API版本
json&pickle模块
json和pickle模块是用来做序列化的,为什么要序列化?序列化可以持久保存状态,还可以跨平台数据交互。
如何序列化?可以使用json或者pickle模块
- json(跨平台) 支持字典(dict)、列表(list)、字符串(str)、整型浮点型(int/float)、bool(True,False),null(None)。
- pickle(纯Python),可以序列化存储python所有的对象,甚至是一个函数,但是只能和Python程序进行交互,其他凭他不支持。
eval不能识别非Python语法的数据类型,所以很多时候要用json
json
json支持跨平台的序列化存储以及反序列化读取。
- 序列化:dump,dumps
- 反序列化:load,loads
1 # json.dumps 表示序列化,会返回一个序列化后的对象。 2 3 import json 4 5 a = [1,2,3,4] 6 7 b = json.dumps(a) 8 9 print(type(b))
1 # json.dump 表示序列化存储到文件中去 2 3 import json 4 5 a = [1,2,3,4,5] 6 7 with open('log.log','w') as f: 8 json.dump(a,f) 9 10 11 # dump 需要两个参数,持久化的存储的对象,第二个是一个可写的文件描述符。
1 # json.loads 从json字符串反序列化为对象 2 import json 3 4 a = '[1, 2, 3, 4]' 5 6 print(type(a)) 7 8 b = json.loads(a) 9 10 print(type(b)) 11 12 print(b)
1 # json.load 从文件中反序列化为对象 2 import json 3 4 with open('hello.txt','r') as f: 5 b = json.load(f) 6 7 print(b)
pickle
pickle仅python支持,在序列化到文件中的时候没需要用b模式打开才可以。
- 序列化:dump,dumps
- 反序列化:load,loads
1 # pickle.dumps 把对象序列化为bytes格式数据 2 import pickle 3 4 a = { 5 'name':'daxin', 6 'age':18, 7 'job':'Linux' 8 } 9 10 b = pickle.dumps(a) 11 print(b) 12 print(type(b))
1 # pickle.dump 序列化到文件中 2 import pickle 3 4 a = { 5 'name':'daxin', 6 'age':18, 7 'job':'Linux' 8 } 9 10 with open('hello.txt','wb') as f: 11 pickle.dump(a,f)
1 # pickle.loads 把bytes对象反序列化 2 import pickle 3 4 b = b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x05\x00\x00\x00daxinq\x02X\x03\x00\x00\x00ageq\x03K\x12X\x03\x00\x00\x00jobq\x04X\x05\x00\x00\x00Linuxq\x05u.' 5 6 a = pickle.loads(b) 7 8 print(a)
1 # pickle.load 从文件中反序列化 2 import pickle 3 4 with open('hello.txt','rb') as f: 5 a = pickle.load(f) 6 7 print(a)
注意:
json序列化后的对象类型为str,pickle序列化后的对象类型为bytes,所以pickle序列化/反序列化时对文件的操作需要用b模式。
备份一个好用的json转换网站:https://www.json.cn/
random模块
顾名思义,用来产生随机数的模块,它有 如下几个常用的方法
random.randint(a,b) # 在a,b之间随机选出一个数(包含a,b本身) random.randrange(a,b,step) # 这里和range的使用方法是相同的 random.choice(iterable) # 在一个可迭代对象中随机选择 random.shuffle(seq) # 原地对序列进行洗牌(随机打乱)