一、复习
看下面一段代码,假如运行结果有问题,那么就需要在每一步计算时,打印一下结果
b = 1
c = 2
d = 3
a = b+c
print(a)
e = a + d
print(e)
执行输出:
3
6
但是线上的程序,是不允许随便 print 的,这个时候,就需要用到 logging 模块
import logging
logging.basicConfig(level=logging.DEBUG,filename = 'userinfo.log')
b = 1
c = 2
d = 3
a = b+c
logging.debug(a)
e = a + d
logging.warning(e)
执行程序,查看文件内容
DEBUG:root:3
WARNING:root:6
总结:
hashlib 摘要 md5 sha 系列
文件的一致性校验
密文的认证
logging 记录日志
两种用法 basicConfig 不常用 getLogger()常用
可以通过一个参数去控制全局的日志输出情况
可以帮助开发者同时向文件和屏幕输出信息
configparser
有分组 section 和有配置项 option 的配置文件,默认.ini 结尾的文件
collections
在基础数据类型之外又额外增加了一些新的数据类型
继续昨天的 collections 模块内容
from collections import OrderedDict
d = OrderedDict()
d['a'] = 1
d['z'] = 2
d['a'] = 3
print(d)
执行输出:
OrderedDict([(‘a’, 3), (‘z’, 2)])
修改值
from collections import OrderedDict
d = OrderedDict()
d['a'] = 1
d['z'] = 2
d['a'] = 3
d['z'] = 0 # 修改值
print(d)
执行输出:
OrderedDict([(‘a’, 3), (‘z’, 0)])
二、defaultdict
kuriko 有如下值集合 [11,22,33,44,55,66,77,88,99,90…],将所有大于 66 的值保存至字典的第一个 key 中,将小于 66 的值保存至第二个 key 的值中。
即: {‘k1’: 大于 66 , ‘k2’: 小于 66}
dic = {}
li = [11,22,33,44,55,66,77,88,99,90]
for i in li:
if i > 66:
if dic.get('k1'):
dic['k1'].append(i)
else:
dic['k1'] = [i]
else:
if dic.get('k2'):
dic['k2'].append(i)
else:
dic['k2'] = [i]
print(dic)
执行输出:
{‘k1’: [77, 88, 99, 90], ‘k2’: [11, 22, 33, 44, 55, 66]}
新的写法,使用 defaultdict
from collections import defaultdict
li = [11,22,33,44,55,66,77,88,99,90]
d = defaultdict(list) # callable() 接收一个可调用对象。给每一个即将要用key,设置一个默认值
for i in li:
if i > 66:
d['k1'].append(i)
else:
d['k2'].append(i)
print(d)
执行输出:
defaultdict(
换成集合方式
from collections import defaultdict
li = [11,22,33,44,55,66,77,88,99,90]
d = defaultdict(set)
for i in li:
if i > 66:
d['k1'].add(i)
else:
d['k2'].add(i)
print(d)
执行输出:
defaultdict(
设置默认值
from collections import defaultdict
d = defaultdict(set)
print(d)
执行输出:
defaultdict(
给它一个值,它就不会用默认值了
from collections import defaultdict
d = defaultdict(set)
print(d['a'])
d['b'] = 10
print(d)
执行输出:
set()
defaultdict(
看下面的代码:
from collections import defaultdict
defaultdict([])
dic = {}
l = []
dic = {'k1':1,'k2':1}
执行报错:
TypeError: first argument must be callable or None
因为这 3 个列表,是同一个内存地址,这样是不对的

查看一个值,如果不存在,就添加
from collections import defaultdict
d = defaultdict(list)
print(d)
print(d[1]) # 值不存在
print(d)
执行输出:
defaultdict(
[]
defaultdict(
defaultdict 只能接收可调用的对象,由于 lambda 也是可调用的
它可以返回任何值
from collections import defaultdict
d = defaultdict(lambda : 5)
print(d[1])
执行输出:5
总结:
默认字典最大的好处就是 永远不会在你使用 key 获取值的时候报错
默认字典 是给 字典中的 value 设置默认值
三、Counter
Counter 类的目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为 key,其计数作为 value。计数值可以是任意的 Interger(包括 0 和负数)。Counter 类和其他语言的 bags 或 multisets 很相似。
from collections import Counter
c = Counter('ab')
print(c)
执行输出:5
Counter({‘a’: 1, ‘b’: 1})
from collections import Counter
c = Counter('which')
c.update('witch') # 使用另一个iterable对象更新
print(c['h']) # 统计h出现了几次
执行输出:3
from collections import Counter
c = Counter('which')
d = Counter('watch')
c.update(d) # 使用另一个Counter对象更新
print(c['h']) # 统计h出现了几次
执行输出:3
这个模块没有作用
在 collections 模块中,主要能用到的数据类型是 namedtuple 和 defaultdict
四、时间模块
和时间有关系的我们就要用到时间模块。在使用模块之前,应该首先导入这个模块。
import time<br>#常用方法
1.time.sleep(secs)
(线程)推迟指定的时间运行。单位为秒。
2.time.time()
获取当前时间戳
时间
计算执行代码的时间 time.time()
让程序停在这里一段时间 sleep
记录时间的格式:
给人看的 格式化的时间
给机器看的 时间戳最接近计算机的时间
计算用户 元组 最接近 python
表示时间的三种方式
在 Python 中,通常有这三种方式来表示时间:时间戳、元组(struct_time)、格式化的时间字符串:
(1)时间戳(timestamp) :通常来说,时间戳表示的是从 1970 年 1 月 1 日 00:00:00 开始按秒计算的偏移量。我们运行“type(time.time())”,返回的是 float 类型。
(2)格式化的时间字符串(Format String): ‘1999-12-06’
%y 两位数的年份表示(00-99)
%Y 四位数的年份表示(000-9999)
%m 月份(01-12)
%d 月内中的一天(0-31)
%H 24小时制小时数(0-23)
%I 12小时制小时数(01-12)
%M 分钟数(00=59)
%S 秒(00-59)
%a 本地简化星期名称
%A 本地完整星期名称
%b 本地简化的月份名称
%B 本地完整的月份名称
%c 本地相应的日期表示和时间表示
%j 年内的一天(001-366)
%p 本地A.M.或P.M.的等价符
%U 一年中的星期数(00-53)星期天为星期的开始
%w 星期(0-6),星期天为星期的开始
%W 一年中的星期数(00-53)星期一为星期的开始
%x 本地相应的日期表示
%X 本地相应的时间表示
%Z 当前时区的名称
%% %号本身
python中时间日期格式化符号:
1970 年 1 月 1 是英国时间,
(3)元组(struct_time) :struct_time 元组共有 9 个元素共九个元素:(年,月,日,时,分,秒,一年中第几周,一年中第几天等)

夏时令,又称“日光节约时制”和“夏令时间”,是一种为节约能源而人为规定地方时间的制度,在这一制度实行期间所采用的统一时间称为“夏令时间”。
在中国,1992 年起,夏令时暂停实行
夏令时,默认为 0
几种格式之间的转换

重点掌握
import time
print(time.time()) # 时间戳时间
print(time.strftime('%Y-%m-%d')) # 字符串格式化时间
print(time.strftime('%m/%d/%Y')) # 月/天/年
print(time.strftime('%H:%M:%S')) # 时:分:秒
print(time.strftime('%Y-%m-%d %H:%M:%S')) # 标准时间
print(time.strftime('%I:%M:%S')) # 十二小时制
print(time.strftime('%x')) # 本地相应的日期表示
print(time.strftime('%X')) # 本地相应的时间表示
print(time.strftime('%x %X')) # 本地日期和时间
print(time.strftime('%c')) # 本地相应的日期表示和时间表示
print(time.localtime()) # 结构化时间
执行输出:
1524563384.8112636
2018-04-24
04/24/2018
17:49:44
2018-04-24 17:49:44
05:49:44
04/24/18
17:49:44
04/24/18 17:49:44
Tue Apr 24 17:49:44 2018
time.struct_time(tm_year=2018, tm_mon=4, tm_mday=24, tm_hour=17, tm_min=49, tm_sec=44, tm_wday=1, tm_yday=114, tm_isdst=0)
最后一个输出的结果,每一个值都有名字,和昨天学的 namedtuple 有点像
假如数据是这样的?
(2018,4,24,10,39,46,1,114,0)
数据就比较难理解了
下面几个要背下来
%Y %m %d %H %M %S
具体符号表示啥意思,请参考上面的《python 中时间日期格式化符号:》
import time
print(time.gmtime()) # 英国的结构化时间
print(time.localtime()) # 本地结构化时间,时间戳时间转格式化时间的中间件
import time
p = time.strptime('2015-8-8','%Y-%m-%d') # 时间字符串->结构化时间
print(p)
print(time.mktime(p)) # 结构化时间->时间戳
print(time.time() - time.mktime(p)) # 当前时间戳-上面的时间戳
执行输出:
time.struct_time(tm_year=2015, tm_mon=8, tm_mday=8, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=5, tm_yday=220, tm_isdst=-1)
1438963200.0
85605561.47843862
ret = time.localtime(1500000000) # 时间戳->结构化时间
print(ret)
print(time.strftime('%Y-%m-%d %H:%M:%S',ret)) # 结构化时间->时间字符串
执行输出:
time.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)
2017-07-14 10:40:00
ret = time.localtime(1600000000)
print(ret)
print(time.strftime('%Y-%m-%d %H:%M:%S',ret))
执行输出:
time.struct_time(tm_year=2020, tm_mon=9, tm_mday=13, tm_hour=20, tm_min=26, tm_sec=40, tm_wday=6, tm_yday=257, tm_isdst=0)
2020-09-13 20:26:40
每隔 3 年,数字会变一次。
ret = time.localtime(0)
print(ret)
print(time.strftime('%Y-%m-%d %H:%M:%S',ret))
执行输出:
time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=8, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=1, tm_isdst=0)
1970-01-01 08:00:00
0 就表示 1970 年
以下内容,不必重点掌握

结构化时间 —> %a %b %d %H:%M:%S %Y 串
time.asctime(结构化时间) 如果不传参数,直接返回当前时间的格式化串
import time<br>print(time.strftime('%c'))
print(time.ctime())
print(time.asctime())
执行输出:
Tue Apr 24 19:29:29 2018
Tue Apr 24 19:29:29 2018
Tue Apr 24 19:29:29 2018
时间戳 —> %a %d %d %H:%M:%S %Y 串
time.ctime(时间戳) 如果不传参数,直接返回当前时间的格式化串
print(time.ctime(1500000000))
ret = time.localtime(2000000000)
print(ret)
print(time.asctime())
print(time.asctime(ret))
执行输出:
Fri Jul 14 10:40:00 2017
time.struct_time(tm_year=2033, tm_mon=5, tm_mday=18, tm_hour=11, tm_min=33, tm_sec=20, tm_wday=2, tm_yday=138, tm_isdst=0)
Tue Apr 24 19:31:37 2018
Wed May 18 11:33:20 2033
五、random 模块
random() 方法返回随机生成的一个实数,默认它在[0,1)范围内。
主要有 2 个应用功能
打印顺序,比如洗牌
随机,比如彩票,验证码
import random
#随机小数
print(random.random()) # 大于0且小于1之间的小数
print(random.uniform(1,3)) #大于1小于3的小数,可以用来发红包
执行输出:
0.7056803343481585
1.4427911142943668
#随机整数
print(random.randint(1,5)) # 大于等于1且小于等于5之间的整数
print(random.randrange(1,10,2)) # 大于等于1且小于10之间的奇数
执行输出:
4
3
print(random.randint(1,5)) # 大于等于1且小于等于5之间的整数
print(random.randrange(1,10,2)) # 大于等于1且小于10之间的奇数
执行输出:
5
1
注意:randrange 顾头不顾尾
取值几率是一样的,比如 3 个数,那么每个数的几率是 1/3
print(random.choice([1,'23',[4,5]])) # #1或者23或者[4,5]
print(random.sample([1,'23',[4,5]],2)) # #列表元素任意2个组合
执行输出:
[4, 5]
[‘23’, [4, 5]]
sample,可以做抽奖。比如公司年终奖,三等奖 10 名,直接抽取 10 个就可以了。
#打乱列表顺序
item=[1,3,5,7,9]
random.shuffle(item) # 打乱次序
print(item)
print(random.shuffle(item))
print(item)
执行输出:
[9, 3, 7, 5, 1]
None
[1, 9, 5, 7, 3]
总结:

六、sys 模块
sys 模块是与 python 解释器交互的一个接口
很多人会把 os 模块和 sys 模块弄混,不知道该用哪一个?
说明一下作用:
os 模块 操作系统打交道的
sys 模块 python 解释器打交道的
相关参数如下:
sys.argv 命令行参数 List,第一个元素是程序本身路径
sys.exit(n) 退出程序,正常退出时 exit(0),错误退出 sys.exit(1)
sys.version 获取 Python 解释程序的版本信息
sys.path 返回模块的搜索路径,初始化时使用 PYTHONPATH 环境变量的值
sys.platform 返回操作系统平台名称
举例:
import sys
print('*'*6)
sys.exit() # 下面的代码不会执行
print('-'*6)
执行输出:
获取 Python 解释程序的版本信息
import sys
print(sys.version)
执行输出:
3.5.4 (v3.5.4:3f56838, Aug 8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)]
返回操作系统平台名称
import sys
print(sys.platform)
执行输出:
win32
win32 表示是 Windows 系统,linux2 表示是 linux 平台
不管 windows 是 64 位还是 32 位,统一显示 win32
sys.path
返回模块的搜索路径,初始化时使用 PYTHONPATH 环境变量的值
import sys
print(sys.path)
执行输出:
[‘E:\python_script\day26’, ‘E:\python_script’, ‘C:\Python35\python35.zip’, ‘C:\Python35\DLLs’, ‘C:\Python35\lib’, ‘C:\Python35’, ‘C:\Python35\lib\site-packages’, ‘C:\Program Files\JetBrains\PyCharm 2018.1.1\helpers\pycharm_matplotlib_backend’]
为什么 Import 的时候,能找到对应的文件呢?
程序在运行起来的时候
启动解释器 加载一些基础的内容 内置函数 内置模块 —>内存里
sys.path 把系统的路径加进去了,所以 import 的时候,能搜索到
sys.argv
可以用 sys.argv 获取当前正在执行的命令行参数的参数列表(list)。
变量解释:
sys.argv[0]当前程序名
sys.argv[1]第一个参数
sys.argv[0]第二个参数
…
test.py 内容如下:
import sys
print(sys.argv)
打开 cmd 窗口,执行 python 代码
python test.py

返回的结果是一个列表,第一个元素就是当前程序名
返回绝对路径

返回后面的参数

比如登录程序,可以通过传参的方式,验证。
test.py 文件内容如下:
import sys
print(sys.argv) # 列表 列表的第一项是当前文件所在的路径
if sys.argv[1] == 'alex' and sys.argv[2] == '3714':
print('登陆成功')
else:
sys.exit()
user = input('>>>')
pwd = input('>>>')
if user == 'alex' and pwd == '3714':
print('登陆成功')
else:
sys.exit()
print('我能完成的功能')
执行程序
python test.py alex 3714

典型用法:
执行一个程序
debug
直接执行
假如输入一个值,结果不是自己想要的
这个时候,就需要 debug 一下
test.py 代码如下:
import logging
num = int(input('>>>'))
a = num * 100
b = a - 10
c = b + 5
print(c)
首先想到的就是用 pycharm 的 debug 功能
但是线上程序代码,不能使用 Pycharm
每一句加 print? 这样是不行的,影响程序使用,用户会察觉的
使用 logging,并使用 level 开关
import sys
import logging
inp = sys.argv[1].upper() if len(sys.argv)>1 else 'WARNING' # 日志级别
logging.basicConfig(level=getattr(logging,inp)) # DEBUG
num = int(input('>>>'))
logging.debug(num)
a = num * 100
logging.debug(a)
b = a - 10
logging.debug(b)
c = b + 5
print(c)
执行输出:

进阶版,格式化输出每一个步骤
import sys
import logging
lev = sys.argv[1].upper() if len(sys.argv) > 1 else 'WARNING'
logging.basicConfig(level=getattr(logging, lev))
num = int(input('>>>').strip())
logging.debug(num)
a = num * 100
logging.debug('{}*100={}'.format(num, a))
b = a - 10
logging.debug('{}-10={}'.format(a, b))
c = b + 5
logging.debug('{}+5={}'.format(b, c))
print(c)
执行输出:

sys 模块中,重点要掌握的就是 argv 和 path
今日作业:
1.要求 生成随机验证码
基础需求: 6位数字验证码 数字可以重复
进阶需求: 字母+数字 4位验证码 数字字母都可以重复
2.y-m-d h:M:S 比如2017-07-08 10:23:41
从当前时间开始 比起y-m-d h:M:S过去了多少年 多少月 多少天 多少小时,多少分,多少秒
答案:
1.1 数字,主要有 0~9 组成,使用 random.randint 生成
print(random.randint(0,9))
执行输出:5
1.2 使用 random 模块取 6 个
import random
random_list = []
for i in range(6): # 取6次
random_num = random.randint(0,9) # 大于等于0且小于等于9之间的整数
random_list.append(random_num) # 追加到列表
print(random_list)
执行输出:
[5, 9, 5, 0, 5, 4]
1.3 将列表转换为字符串,使用 join。注意,使用 join,必须保证列表中的每一个元素为字符串类型
import random
random_list = []
for i in range(6): # 取6次
random_num = random.randint(0,9) # 大于等于0且小于等于9之间的整数
random_list.append(str(random_num)) # 将元素转换为str并追加到列表
ret = ''.join(random_list)
print(ret)
执行输出:
615411
1.4 进阶需求,如何输出所有的字母呢?需要用到 ascci 码
capital = [chr(i) for i in range(65,91)] #所有大写字母
lowercase = [chr(i) for i in range(97,123)] #所有小写字母
print(capital)
print(lowercase)
执行输出:
[‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’, ‘G’, ‘H’, ‘I’, ‘J’, ‘K’, ‘L’, ‘M’, ‘N’, ‘O’, ‘P’, ‘Q’, ‘R’, ‘S’, ‘T’, ‘U’, ‘V’, ‘W’, ‘X’, ‘Y’, ‘Z’]
[‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘g’, ‘h’, ‘i’, ‘j’, ‘k’, ‘l’, ‘m’, ‘n’, ‘o’, ‘p’, ‘q’, ‘r’, ‘s’, ‘t’, ‘u’, ‘v’, ‘w’, ‘x’, ‘y’, ‘z’]
1.6 将数字和字母组合,生成 4 位
import random
for i in range(4): # 取4位
lowercase = chr(random.randint(97,123)) #所有小写字母
capital = chr(random.randint(65,91)) #所有大写字母
number = str(random.randint(0,9)) # 所有数字
print(lowercase,capital,number)
执行输出:
w J 8
z O 5
e T 6
u S 9
1.7 将随机数添加到列表中,并转化为字符串
import random
code = []
for i in range(4): # 取4位
lowercase = chr(random.randint(97,123)) #所有小写字母
capital = chr(random.randint(65,91)) #所有大写字母
number = str(random.randint(0,9)) # 所有数字
code.append(random.choice([lowercase,capital,number])) # 有3个元素,几率为1/3
ret = ''.join(code) # 列表转换为字符串
print(ret)
执行输出:
7JmY
2.1 既然是比较 2 个时间的差值,就需要转化为时间戳,再做减法
将字符串时间转换为时间戳
import time
before = time.strptime("2017-07-08 10:23:41",'%Y-%m-%d %H:%M:%S')
before_stamp = time.mktime(before)
print(before_stamp)
执行输出:
1499480621.0
2.2 将 before_stamp 和当前时间戳相减
import time
before = time.strptime("2017-07-08 10:23:41",'%Y-%m-%d %H:%M:%S')
before_stamp = time.mktime(before)
diff_stamp = time.time() - before_stamp # 当前时间戳-指定时间戳
print(diff_stamp)
执行输出:
25095583.435019016
2.3 差值转换为英国的结构化时间
import time
before = time.strptime("2017-07-08 10:23:41",'%Y-%m-%d %H:%M:%S')
before_stamp = time.mktime(before)
diff_stamp = time.time() - before_stamp # 当前时间戳-指定时间戳
struct_time=time.gmtime(diff_stamp) # 差值转换为英国的结构化时间
print(struct_time)
time.struct_time(tm_year=1970, tm_mon=10, tm_mday=18, tm_hour=11, tm_min=2, tm_sec=53, tm_wday=6, tm_yday=291, tm_isdst=0)
2.4 时间戳为 0 时,表示 1970-01-01 0:00:00
将 struct_time 时间,分别取出年,月,日,时,分,秒 减去 1970-01-01 08:00:00,就可以得到差值为多少年,月,日,时,分,秒
import time
before = time.strptime("2017-07-08 10:23:41",'%Y-%m-%d %H:%M:%S') # 之前的时间
before_stamp = time.mktime(before) # 转换为时间戳
diff_stamp = time.time() - before_stamp # 当前时间戳-指定时间戳
struct_time=time.gmtime(diff_stamp) # 差值转换为英国的结构化时间
#1970-01-01 0:00:00 # 英国时间
year = struct_time.tm_year - 1970
month = struct_time.tm_mon - 1
day = struct_time.tm_mday - 1
hour = struct_time.tm_hour
minute = struct_time.tm_min
second = struct_time.tm_sec
print('过去了{}年{}月{}日{}时{}分{}秒'.format(year,month,day,hour,minute,second))
执行输出:
过去了 0 年 9 月 17 日 3 时 20 分 51 秒