一. 昨日内容回顾
time datetime
1. time.time()
2. time.strftime("%Y-%m-%d %H:%M:%S")
3. time.localtime()
datetime
日志:logging
低配
标配
高配
# 高配版日志
import os
import logging.config # 这是一个整体
# 定义三种日志输出格式 开始
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]'
'[%(levelname)s][%(message)s]' #其中name为getlogger指定的名字
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'
# 定义日志输出格式 结束
logfile_dir = os.path.dirname(os.path.abspath(__file__)) # log文件的目录
logfile_name = 'all2.log' # log文件名
# 如果不存在定义的日志目录就创建一个
if not os.path.isdir(logfile_dir):
os.mkdir(logfile_dir)
# log文件的全路径
logfile_path = os.path.join(logfile_dir, logfile_name)
# log配置字典
LOGGING_DIC = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': standard_format
},
'simple': {
'format': simple_format
},
},
'filters': {},
'handlers': {
# 打印到终端的日志
# 屏幕句柄
'screen': {
'level': 'DEBUG',
'class': 'logging.StreamHandler', # 打印到屏幕
'formatter': 'simple'
},
# 打印到文件的日志,收集info及以上的日志
# 这里可以添加其他文件句柄,比如 "staff",后面记得也要修改
'file': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件
'formatter': 'standard',
'filename': logfile_path, # 日志文件
'maxBytes': 1024*1024*5, # 日志大小 5M
'backupCount': 5,
'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了
},
},
'loggers': {
#logging.getLogger(__name__)拿到的logger配置
'': {
'handlers': ['screen', 'file'], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
'level': 'DEBUG', # 总级别,一定要设置最低的,即debug
'propagate': True, # 向上(更高level的logger)传递
},
},
}
def load_my_logging_cfg():
logging.config.dictConfig(LOGGING_DIC) # 导入上面定义的logging配置
# logger = logging.getLogger(__name__) # 生成一个log实例
logger = logging.getLogger("转账业务")
logger.info('It works!') # 记录该文件的运行状态
if __name__ == '__main__':
load_my_logging_cfg()
# 上面的程序要再加一个文件日志,只需在以下几步添加或修改:
# 第一步:
# logfile_name = 'all2.log' # log文件名
#
# 第二步:
# 'file': {
# 'level': 'DEBUG',
# 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件
# 'formatter': 'standard',
# # 第三步:
# 'filename': logfile_path, # 日志文件
# 'maxBytes': 1024*1024*5, # 日志大小 5M
# 'backupCount': 5,
# 'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了
# },
# },
# 第四步:
# 列表里对应修改。比如在第二步加了 "staff",这里要添加 "staff"
# 'handlers': ['stream', 'file'],
# 第五步
# logger = logging.getLogger("转账业务") # 这里的参数可按需求随便改
# logger.info('It works!') # 这里不仅参数可以随便改,logger.info也可以改为 logger.warning 之类的
# 可以有两个 LOGGING_DIC, 然后两个 def load_my_logging_cfg() 当然函数名要不一样,相应那五步也要改过来
def func():
"""
这是文档说明
:return:
"""
print(__name__)
print(func.__name__) # func
print(func.__doc__)
# 这是文档说明
# :return:
def func():
"""
这是文档说明
:return:
"""
print(func.__name__)
func() # func
# 序列化模块(重要)
# 网络数据传输只能通过 bytes 类型
# 文件写入内容(注意不是存储)即可以是 bytes, 也可以是 str
dic = {"name": "太白金星", "hobby": ["戒烟", "烫不了头", "戒酒"]}
s1 = str(dic)
b1 = s1.encode("utf-8")
print(b1)
# b"{'name': 'xe5xa4xaaxe7x99xbdxe9x87x91xe6x98x9f', 'hobby': ['xe6x88x92xe7x83x9f', 'xe7x83xabxe4xb8x8dxe4xbax86xe5xa4xb4', 'xe6x88x92xe9x85x92']}"
s2 = b1.decode("utf-8")
# print(s2, type(s2))
dic2 = eval(s2)
print(dic2, type(dic2))
# {'name': '太白金星', 'hobby': ['戒烟', '烫不了头', '戒酒']} <class 'dict'>
# 但这是网络传输,使用 eval() 很危险,容易被黑客截取并替换成病毒文件
# 因此需要一个功能,能将数据转化成可以通过网络传输的 bytes
# 序列化过程——将数据(数据结构,非字符串) 转化成 特殊的字符串(可以用于网络传输)
# 反序列化过程——另外还得将这个特殊的字符串 转化成 原来的数据结构
# Python中的序列化模块有三种:
# import json
# json 序列化模块是所有语言通用的一种标准,也是一种数据转化格式
# 也就是说 Python 中的字典,可以通过 json 模块转化成 bytes 来进行网络传输成 java 的字典
# str, int, bool, dict, list(tuple), None 没有集合
# import pickle
# pickle 也是序列化模块,但是只支持Python语言中所有数据类型(包括对象)的网络传输(用的少)
# 写入文件时,可以写入多个
# import shelve # 了解
# shelve 序列化模块,只支持Python,一般与文件相关,即通过它把数据写入文件,拿出来后再反解成原数据类型
# json——安全模式的网络传输
# 项目中一般使用 json 来写
# 第一对方法:dumps loads
# import json
dic = {"name": "太白金星", "hobby": ["戒烟", "烫不了头", "戒酒"]}
# json的序列化过程
s = json.dumps(dic)
print(s, type(s))
# # {"name": "u592au767du91d1u661f", "hobby": ["u6212u70df", "u70ebu4e0du4e86u5934", "u6212u9152"]} <class 'str'>
s = json.dumps(dic, ensure_ascii=False)
print(s)
# {"name": "太白金星", "hobby": ["戒烟", "烫不了头", "戒酒"]}
# json的反序列化过程
dic1 = json.loads(s)
print(dic1, type(dic1))
# {'name': '太白金星', 'hobby': ['戒烟', '烫不了头', '戒酒']} <class 'dict'>
# 注意
dic = {"name": "太白金星", "hobby": ["戒烟", "烫不了头", "戒酒"]}
print(dic)
# {'name': '太白金星', 'hobby': ['戒烟', '烫不了头', '戒酒']}
# 字符串都是单引号,不过写的是双引号还是单引号
# 而上面的json.dumps 用法结果是双引号
# 第二对 dump load 与文件相关
dic = {"name": "太白金星", "hobby": ["戒烟", "烫不了头", "戒酒"]}
with open("序列化.json", encoding="utf-8", mode="w") as f:
# json.dump(dic, f)
json.dump(dic, f, ensure_ascii=False)
with open("序列化.json", encoding="utf-8") as f2:
ret = json.load(f2)
print(ret, type(ret))
# {'name': '太白金星', 'hobby': ['戒烟', '烫不了头', '戒酒']} <class 'dict'>
# 参数讲解
# ensure_ascii=False 显示中文
# sort_keys 按键的首字母的 ascii 排序
import json
dic = {"name": "太白金星",
"hobby": ["戒烟", "烫不了头", "戒酒"],
"age": 18,
"money": "一个亿"}
s = json.dumps(dic, ensure_ascii=False, sort_keys=True)
print(s)
# # {"age": 18, "hobby": ["戒烟", "烫不了头", "戒酒"], "money": "一个亿", "name": "太白金星"}
# json 与 bytes 的区别
# bytes 只能操作str,用于网络传输
# json 可以操作 str, int, bool, dict, list(tuple), None 没有集合,用于网络传输,写入文件
# 通过json将多个字典写入一个文件
# 如果用 dump load 一个文件只能写入一个数据结构!!!
import json
dic1 = {"name": "abc"}
dic2 = {"name": "def"}
dic3 = {"name": "xyz"}
# 下面这样不行
with open("多个字典.json", encoding="utf-8", mode="w") as f:
json.dump(dic1, f)
json.dump(dic2, f)
json.dump(dic3, f)
with open("多个字典.json", encoding="utf-8") as f1:
ret1 = json.load(f1)
ret2 = json.load(f1)
ret3 = json.load(f1)
import json
dic1 = {"name": "abc"}
dic2 = {"name": "def"}
dic3 = {"name": "xyz"}
with open("多个字典.json", encoding="utf-8", mode="w") as f:
f.write(json.dumps(dic1) + "
")
f.write(json.dumps(dic2) + "
")
f.write(json.dumps(dic3) + "
")
# 上面有结果,但是会飘红,不影响后面的操作
with open("多个字典.json", encoding="utf-8") as f1:
for line in f1:
print(json.loads(line))
# {"name": "abc"}
# {"name": "def"}
# {"name": "xyz"}
# 总结
# dumps loads 用于网络传输和多个数据写入文件
# dump load 只能用于一个数据结构写入文件
# 一个小细节
import json
dic = {1: "alex"}
t = json.dumps(dic)
print(t) # {"1": "alex"}
# 发现数字1变成字符串形式了
print(json.loads(t)) # {'1': 'alex'}
# 还原过后已经没有整数型了
# 算是一个 bug,一个坑
# pickle
import pickle
# dums, loads 用于网络传输,把所有数据类型转化成 bytes
dic = {"name": "太白金星",
"hobby": ["戒烟", "烫不了头", "戒酒"],
"age": 18,
"money": "一个亿"}
s1 = pickle.dumps(dic)
# print(s1)
dic2 = pickle.loads(s1)
print(dic2, type(dic2))
dic = {"name": "太白金星",
"hobby": ["戒烟", "烫不了头", "戒酒"],
"age": 18,
"money": "一个亿"}
with open("p1.pickle", mode="wb") as f:
pickle.dump(dic, f)
with open("p1.pickle", mode="rb") as f1:
ret = pickle.load(f1)
print(ret, type(ret))
# {'name': '太白金星', 'hobby': ['戒烟', '烫不了头', '戒酒'], 'age': 18, 'money': '一个亿'} <class 'dict'>
# 利用 dump 和 load 将多个数据写入文件,json的不行
dic1 = {"name": "abc"}
dic2 = {"name": "def"}
dic3 = {"name": "xyz"}
# 注意这里不用写明 encoding,因为带 b 的不能用!!!
with open("p2.pickle", mode="wb") as f:
pickle.dump(dic1, f)
pickle.dump(dic2, f)
pickle.dump(dic3, f)
with open("p2.pickle", mode="rb") as f1:
ret1 = pickle.load(f1)
ret2 = pickle.load(f1)
ret3 = pickle.load(f1)
print(ret1, ret2, ret3)
# {'name': 'abc'} {'name': 'def'} {'name': 'xyz'}
def func():
print(666)
with open("p3.pickle", mode="wb") as f:
pickle.dump(func, f)
with open("p3.pickle", mode="rb") as f1:
ret = pickle.load(f1)
ret() # 666
# shelve
# helve也是python提供给我们的序列化工具,比pickle用起来更简单一些。
# shelve只提供给我们一个open方法,是用key来访问的,使用起来和字典类似。
import shelve
f = shelve.open('shelve_file')
f['key'] = {'int':10, 'float':9.5, 'string':'Sample data'} #直接对文件句柄操作,就可以存入数据
f.close()
# 上面相当于给文件写入一个字典,{"key": {'int':10, 'float':9.5, 'string':'Sample data'}}
# 结果有三个文件
f = shelve.open('shelve_file')
existing = f1['key'] #取出数据的时候也只需要直接用key获取即可,但是如果key不存在会报错
f1.close()
print(existing)
# 这个模块有个限制,它不支持多个应用同一时间往同一个DB进行写操作。
# 所以当应用只进行读操作,可以让shelve通过只读方式打开DB
import shelve
f = shelve.open('shelve_file', flag='r')
existing = f['key']
f.close()
print(existing)
# 由于shelve在默认情况下是不会记录待持久化对象的任何修改的
# 所以在shelve.open()时候需要修改默认参数,否则对象的修改不会保存。
import shelve
f1 = shelve.open('shelve_file')
print(f1['key'])
f1['key']['new_value'] = 'this was not here before'
f1.close()
# 如果想对 shelve 文件进行修改,必须要加这个参数
f2 = shelve.open('shelve_file', writeback=True)
print(f2['key'])
f2['key']['new_value'] = 'this was not here before'
f2.close()
# 加密模块 摘要算法 一堆加密算法的集合体
# hshlib的规则——将 str 通过算法得到一串等长度的 数字
# 1.不同的字符串转化成的数字肯定不同
# 2.相同的字符串即使在不同的计算机,只要使用相同的加密方式,转化成的数字一定相同
# 3.hashlib 加密不可逆,不能破解
# 给密码加密
import hashlib
# md5
ret = hashlib.md5()
ret.update("好123".encode("utf-8"))
print(ret.hexdigest())
# 51cb75f82eceb17b86f019e01618d75e
ret = hashlib.md5()
ret.update("asjdlkajlkajsdlkajljlajsalsj".encode("utf-8"))
print(ret.hexdigest())
# 77e2bcc002a6a2a957519303d16a976a
# 可以看出,不管字符串多长,它都是转化成一样长的数字
# 加盐——让密码更复杂
ret = hashlib.md5("老男孩教育".encode("utf-8"))
ret.update("123456".encode("utf-8"))
print(ret.hexdigest())
# b08404ec951d75b6da37fdd1bfb8c1e9
# 动态盐
username = input("请输入用户名: ").strip()
# ret = hashlib.md5(username.encode("utf-8"))
ret = hashlib.md5(username[::2].encode("utf-8"))
ret.update("123456".encode("utf-8"))
print(ret.hexdigest())
# asdak
# 331c5caa07f18d43b7c6302202bd4d2d
# md5 加密效率快,通用,安全性相对差
# sha 系列,算法更好,安全性高,效率低,耗时长
# sha系列
ret = hashlib.sha512()
ret.update("aksdjalksjdalkj".encode("utf-8"))
print(ret.hexdigest())
# 093bc658d3b1a40ba2f4b41a8f86cca8cb79f7808e52
# 75e69303988afd938722f97d5ac4565f2a494d75dabcdbca6
# 1e446fd7c4a14c29bfa711a7a34fa84d1e2
# 加盐
ret = hashlib.sha512("好好学习".encode("utf-8"))
ret.update("aksdjalksjdalkj".encode("utf-8"))
print(ret.hexdigest())
# a17ae4ede113d8969df70b5a6547b41164dfcd56423d
# 4d2a761f625558f22b4791f4808132db994cbc9ec76ccdb
# 6b3a44d17b71e043fbcbc5584f96f9a38b88d
# 动态盐
username = input("请输入用户名:").strip()
ret = hashlib.sha512(username.encode("utf-8"))
ret.update("aksdjalksjdalkj".encode("utf-8"))
print(ret.hexdigest())
# askdj
# ee97bc4ad328e098fef08395c07309adf7746e37dec81d
# 2e123d34fa81ffceaaa9c15f4cb2ba8544c5a9217640e0e
# 48953f12dd0ac820176a3982260ad70fc8f
# 文件校验
def check_md5(file):
ret = hashlib.md5()
with open(file, encoding="utf-8", mode="rb") as f:
ret.update(f.read())
return ret.hexdigest()
print(check_md5("文件校验1"))
print(check_md5("文件校验2")) # 注意一定要有两个文件,内容一样
# "文件校验1与当前文件在同一个文件夹
# 这里有个问题,如果文件太大占内存,因此用 for循环hao
ret = hashlib.md5()
ret.update("好好学习并且天天向上".encode("utf-8"))
print(ret.hexdigest())
ret.update("好好学习".encode("utf-8"))
ret.update("并且".encode("utf-8"))
ret.update("天天向上".encode("utf-8"))
print(ret.hexdigest())
# 上面两个结果一样
# 大文件校验
# 因此,文件较大时,可以这样写
# 按照一行一行读取
def check_md5(file):
ret = hashlib.md5()
with open(file, mode="rb") as f:
for line in f:
ret.update(line)
return ret.hexdigest()
print(check_md5("test"))
print(check_md5("test01"))
# 8a9a779e5bacbd752fb5deb05581f86e
# 8a9a779e5bacbd752fb5deb05581f86e
# 按照字节读取
def check_md5(file):
ret = hashlib.md5()
with open(file, mode="rb") as f:
while 1:
content = f.read(1024)
# 这个表示每次最多读取这么多字节,而不是每次读取1024个字节
if content: # 如果真表示一直能取到内容
ret.update(content)
else:
break
# 因此这里文件的最后内容不够1024个字节时,把剩下的读取出来,然后退出
return ret.hexdigest()
print(check_md5("test"))
print(check_md5("test01"))
# 8a9a779e5bacbd752fb5deb05581f86e
# 8a9a779e5bacbd752fb5deb05581f86e
os sys 模块
os模块是与操作系统交互的一个接口
工作目录,父级目录,当前目录都是此文件从属的文件夹路径
绝对路径——从根目录到当前文件的文件名
相对路径——同一个文件夹下的文件的相对关系
当前执行这个python文件的工作目录相关的工作路径
获取当前工作目录,即当前python脚本工作的目录路径
os.getcwd()
改变当前脚本工作目录;相当于shell下cd
os.chdir("dirname")
返回当前目录: ('.')
os.curdir
获取当前目录的父目录字符串名:('..')
os.pardir
和文件夹相关
可生成多层递归目录
os.makedirs('dirname1/dirname2')
若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.removedirs('dirname1')
生成单级目录;相当于shell中mkdir dirname
os.mkdir('dirname')
删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir
dirname
os.rmdir('dirname')
列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
os.listdir('dirname')
和文件相关
删除一个文件
os.remove()
重命名文件/目录
os.rename("oldname","newname")
获取文件/目录信息
os.stat('path/filename')
path系列,和路径相关
返回path规范化的绝对路径
import os
os.path.abspath(path)
print(os.path.abspath("模块.py")) # 获取绝对路径
G:ATM模块.py
将path分割成目录和文件名二元组返回
os.path.split(path)
返回path的目录。其实就是os.path.split(path)的第一个元素
os.path.dirname(path)
返回path最后的文件名。如何path以/或结尾,那么就会返回空值,
即os.path.split(path)的第二个元素。
os.path.basename(path)
如果path存在,返回True;如果path不存在,返回False
os.path.exists(path)
如果path是绝对路径,返回True
os.path.isabs(path)
如果path是一个存在的文件,返回True。否则返回False
os.path.isfile(path)
如果path是一个存在的目录,则返回True。否则返回False
os.path.isdir(path)
将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
os.path.join(path1[, path2[, ...]])
返回path所指向的文件或者目录的最后访问时间
os.path.getatime(path)
返回path所指向的文件或者目录的最后修改时间
os.path.getmtime(path)
返回path的大小
os.path.getsize(path)
sys模块是与Python解释器交互的一个接口
sys.argv 命令行参数List,第一个元素是程序本身路径
sys.exit(n) 退出程序,正常退出时exit(0),错误退出sys.exit(1)
sys.version 获取Python解释程序的版本信息
sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform 返回操作系统平台名称
import sys
try:
sys.exit(1)
except SystemExit as e:
print(e)
collections模块
在内置数据类型(dict、list、set、tuple)的基础上
collections模块还提供了几个额外的数据类型:
Counter、deque、defaultdict、namedtuple和OrderedDict等。
# 1.namedtuple: 生成可以使用名字来访问元素内容的tuple
# 命名元组
# tuple可以表示不变集合,例如,一个点的二维坐标就可以表示成:
# p = (1, 2)
# 看到(1, 2),很难看出这个tuple是用来表示一个坐标的,所以应该这样:
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(1, 2)
print(p) # Point(x=1, y=2)
print(p[0]) # 1
print(p.x) # 1
print(p.x + p.y) # 3
# 类似的,如果要用坐标和半径表示一个圆,也可以用namedtuple定义:
# namedtuple('名称', [属性list]):
Circle = namedtuple('Circle', ['x', 'y', 'r'])
# 2.deque: 双向队列,可以快速的从另外一侧追加和推出对象,也是数据类型
# 比如购物时只有前10人有优惠。
# 使用list存储数据时,按索引访问元素很快
# 但是插入和删除元素就很慢了,因为list是线性存储
# 数据量大的时候,插入和删除效率很低。
# 而deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:
from collections import deque
q = deque(['a', 'b', 'c'])
q.append('x') # 从右添加
q.appendleft('y') # 从左添加
print(q)
# deque(['y', 'a', 'b', 'c', 'x'])
# pop()也一样
# 3.defaultdict: 带有默认值的字典
# 有如下值集合 [11,22,33,44,55,66,77,88,99,90...]
# 将所有大于 66 的值保存至字典的第一个key中,将小于 66 的值保存至第二个key的值中。
li = [11,22,33,44,55,77,88,99,90]
# 原生字典解决办法
result = {}
for row in li:
if row > 66:
if 'key1' not in result:
result['key1'] = []
result['key1'].append(row)
else:
if 'key2' not in result:
result['key2'] = []
result['key2'].append(row)
print(result)
# defaultdict字典解决办法
from collections import defaultdict
values = [11, 22, 33,44,55,66,77,88,99,90]
my_dict = defaultdict(list)
# print(my_dict["key1"]) # []
# print(my_dict["key2"]) # []
for value in values:
if value > 66:
my_dict["key1"].append(value)
else:
my_dict["key2"].append(value)
print(my_dict)
# defaultdict(<class 'list'>,
# {'key2': [11, 22, 33, 44, 55, 66], 'key1': [77, 88, 99, 90]})
# 构建一个字典,字典的key 从1-100,对应的值都是666
# 第一种方法
dic = {}
for i in range(1, 101):
dic[i] = 666
print(dic)
# 第二种方法
dic = dict.fromkeys(range(1, 101), 666)
print(dic)
# 第三种方法
# 字典推导式
print({key:666 for key in range(1, 101)})
# 第四种方法
from collections import defaultdict
def func():
return 666
my_dict = defaultdict(func)
for i in range(1, 101):
my_dict[i]
print(my_dict)
# 4.Counter: 计数器,主要用来计数
# Counter类的目的是用来跟踪值出现的次数。
# 它是一个无序的容器类型,以字典的键值对形式存储
# 其中元素作为key,其计数作为value。
# 计数值可以是任意的Interger(包括0和负数)。
# Counter类和其他语言的bags或multisets很相似。
from collections import Counter
c = Counter('abcdeabcdabcaba')
print(c)
# Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1})