collections、random、hashlib、configparser、logging模块
collections模块
在内置数据类型(dict、list、set、tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter、deque、defaultdict、namedtuple和OrderedDict等。
- namedtuple:生成可以使用名字来访问元素内容的tuple
- deque:双端队列,可以快速的从另外一侧追加和推出对象
- Counter:计数器,主要用来计数
- OrderedDict:有序字典
- defaultdict:带有默认值的字典
namedtuple
我们知道tuple
可以表示不变集合,例如,一个点的二维坐标就可以表示成:
p = (1, 2)
但是,看到(1, 2),很难看出这个tuple是用来表示一个坐标的。
这时,namedtuple
就派上了用场:
from collections import namedtuple Point = namedtuple("Point", ["x", "y"]) p = Point(1, 2) print(p.x) # 1 print(p.y) # 2
类似的,如果要用坐标和半径表示一个圆,也可以用namedtuple
定义:
#namedtuple('名称', [属性list]): Circle = namedtuple('Circle', ['x', 'y', 'r'])
queue:队列
#put()放值、get()取值 import queue
q = queue.Queue() q.put([1,2,3]) q.put(5) q.put(6) print(q.get()) #[1, 2, 3] print(q.get()) #5 print(q.get()) #6
deque
使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低。
deque是为了高效实现插入和删除操作的双向列表,适合用于队列和堆栈:
- 队列 :先进先出
- 堆栈 :先进后出
from collections import deque dq = deque([1, 3]) dq.append("a") # [1, 3, "a"] 从后面放数据 dq.appendleft("b") # ["b", 1, 3, "a"] 从前面放数据 dq.insert(2, 2) # ["b", 1, 2, 3,"a"] 在第二个位置插入2 print(dq) # deque(['b', 1, 2, 3, 'a']) print(dq.pop()) # a 从后面取数据 print(dq.popleft()) # b 从前面取数据 print(dq) # deque([1, 2, 3])
OrderedDict
使用dict时,Key是无序的。在对dict做迭代时,我们无法确定Key的顺序;如果要保持Key的顺序,可以用 OrderedDict。
from collections import OrderedDict # dict的Key是无序的 dic = dict([("a", 1), ("b", 2), ("c", 3)]) print(dic) # 可能是 {'a': 1, 'c': 3, 'b': 2} # OrderedDict的Key是有序的 o_dic = OrderedDict([("a", 1), ("b", 2), ("c", 3)]) print(o_dic) # OrderedDict([('a', 1), ('b', 2), ('c', 3)])
注意,OrderedDict
的Key会按照插入的顺序排列,不是Key本身排序:
from collections import OrderedDict o_dic = OrderedDict() o_dic["a"] = 1 o_dic["b"] = 2 o_dic["c"] = 3 print(o_dic.keys()) # 按照插入的Key的顺序返回 # odict_keys(['a', 'b', 'c'])
defaultdict
有如下值集合 [11, 22, 33, 44, 55, 66, 77, 88, 99],将所有大于 66 的值保存至字典的第一个key中,将小于 66 的值保存至第二个key的值中。
from collections import defaultdict l = [11, 22, 33, 44, 55, 66, 77, 88, 99] dic = defaultdict(list) for item in l: if item > 66: dic["k1"].append(item) else: dic["k2"].append(item) print(dic) #defaultdict(<class "list">, {"k2": [11, 22, 33, 44, 55, 66], "k1": [77, 88, 99]})
Counter
Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key,其计数作为value。计数值可以是任意的Interger(包括0和负数)。
from collections import Counter c = Counter('abcdeabcdabcaba') print(c) # Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1})
random模块
import random # 随机小数 print(random.random()) # 大于0小于1的小数 print(random.uniform(6, 8)) # 大于6小于8的小数 # 随机整数 print(random.randint(1, 5)) # 大于等于1小于等于5的整数 print(random.randrange(1, 10, 2)) # 大于等于1且小于10之间的奇数,2为步长 # 随机选择一个返回 print(random.choice([1, "a", [2, 3]])) # 1或者"a"或者[2, 3] # 随机选择多个返回,返回的个数为函数的第二个参数 print(random.sample([1, "a", [2, 3]], 2)) # 列表元素任意2个组合 # 打乱列表顺序 item = [1, 3, 5, 7, 9] random.shuffle(item) # 打乱顺序 print(item)
import random def random_verify_code(): code = "" for i in range(5): num = random.randint(0, 9) upper_alpha = chr(random.randint(65, 90)) lower_alpha = chr(random.randint(97, 122)) item = random.choice([num, upper_alpha, lower_alpha]) code = "".join([code, str(item)]) return code print(random_verify_code())
hashlib模块
Python的hashlib模块提供了常见的摘要算法;如MD5,SHA1等...
以常见的摘要算法MD5为例,计算出一个字符串的MD5值:
import hashlib md5 = hashlib.md5() md5.update(b"how to use md5 in python hashlib?") print(md5.hexdigest())
如果数据量很大,可以分块多次调用update(),最后计算的结果是一样的:
import hashlib md5 = hashlib.md5() md5.update(b"how to use md5 in ") md5.update(b"python hashlib?") print(md5.hexdigest())
由于常用口令的 md5 值很容易被计算出来,所以,要确保存储的用户口令不是那些已经被计算出来的常用口令的 md5,这一方法通过对原始口令加一个复杂字符串来实现,俗称“加盐”:
import hashlib md5 = hashlib.md5(bytes("salt", encoding="utf-8")) # "salt"可替换成任意字符串 md5.update(b"how to use md5 in python hashlib?") print(md5.hexdigest())
经过加盐处理的MD5口令,只要Salt不被黑客知道,即使用户输入简单口令,也很难通过MD5反推明文口令。
但是如果有两个用户都使用了相同的简单口令比如123456,在数据库中,将存储两条相同的MD5值,这说明这两个用户的口令是一样的。有没有办法让使用相同口令的用户存储不同的MD5呢?
如果假定用户名唯一,就可以通过把用户名作为Salt的一部分来计算MD5,从而实现相同口令的用户也存储不同的MD5。
摘要算法在很多地方都有广泛的应用。要注意摘要算法不属于加解密算法,不能用于加密(因为无法通过摘要反推明文),只能用于防篡改,但是它的单向计算特性决定了可以在不存储明文口令的情况下验证用户口令。
import hashlib username = input("username:") password = input("password:") md5 = hashlib.md5(bytes(username, encoding="utf-8")) md5.update(bytes(password, encoding="utf-8")) md5_password = md5.hexdigest() if username == "pd" and md5_password == "bf55109f7e511ebf6d8f1f43a5a96539": print("登录成功")
configparser模块
该模块适用于配置文件的格式与windows ini文件类似,可以包含一个或多个节(section),每个节可以有多个参数(键=值)。
创建文件
来看一个好多软件的常见文档格式如下:
[DEFAULT] ServerAliveInterval = 45 Compression = yes CompressionLevel = 9 ForwardX11 = yes [bitbucket.org] User = hg [topsecret.server.com] Port = 50022 ForwardX11 = no
如果想用python生成一个这样的文档怎么做呢?
import configparser config = configparser.ConfigParser() config['DEFAULT'] = {'ServerAliveInterval': '45', 'Compression': 'yes', 'CompressionLevel': '9', 'ForwardX11': 'yes'} config['bitbucket.org'] = {'User': 'hg'} config['topsecret.server.com'] = {'Host Port': '50022', 'ForwardX11': 'no'} with open('example.ini', 'w') as configfile: config.write(configfile)
增删改操作
import configparser config = configparser.ConfigParser() #查找文件内容,基于字典的形式 print(config.sections()) # [] 没读文件,得到的是空 config.read('example.ini', encoding='utf-8') #读文件 print(config.sections()) # 获取所有节点 ['bitbucket.org', 'topsecret.server.com'] print(config.items("bitbucket.org")) # 获取指定节点下所有的键值对(如果有DEFAULT节点,默认也获取) print(config.get('bitbucket.org','compression')) # 获取指定节点下指定key的值 print(config['bitbucket.org']["user"]) # 获取指定节点下指定key的值 print('bytebong.com' in config) # False print('bitbucket.org' in config) # True for key in config['bitbucket.org']: # 如果有DEFAULT节点的话,默认也同时获取key print(key) print(config.options('bitbucket.org')) # 返回的是由key组成的列表
检查、添加、修改、删除
import configparser config = configparser.ConfigParser() config.read("example.ini", encoding="utf-8") # 检查节点 has_sec = config.has_section("bitbucket.org") print(has_sec) # True # 添加新节点 config.add_section("xxx") config.write(open("example.ini", "w")) # 修原有改节点键值对 config.set('section', 'key', 'value') config.write(open('filename', 'w')) # 添加新节点,同时定义一对键值对 config.add_section('new_section') config.set('new_section', 'new_key', "new_value") config.write(open('filename', 'w')) # 删除节点 config.remove_section('bitbucket.org') config.write(open("example.ini", "w"))
logging模块
用于便捷记录日志且线性安全的模块
1、单文件日志(不推荐)
import logging logging.basicConfig(filename="log.log", level=logging.DEBUG, format="%(asctime)s - %(module)s/%(filename)s[line:%(lineno)d] - %(levelname)s:%(message)s", datefmt="%d-%b-%Y %H:%M:%S", filemode="w") logging.debug("debug message") #排错信息 logging.info("info message") #正常信息 logging.warning("warning message") #警告信息 logging.error("error message") #错误信息 logging.critical("critical message") #严重错误信息
2、多文件日志(推荐)
对于上述记录日志的功能,只能将日志记录在单文件中,如果想要设置多个日志文件,logging.basicConfig将无法完成,需要自定义文件和日志操作对象。
logging.basicConfig() 函数中可通过具体参数来更改logging模块默认行为,可用参数有: filename:--用指定的文件名创建FiledHandler,这样日志会被存储在指定的文件中。 filemode:--文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。 format:----指定handler使用的日志显示格式。 datefmt:---指定日期时间格式。 level:-----设置rootlogger(后边会讲解具体概念)的日志级别 stream:----用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件(f=open(‘test.log’,’w’)),默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。 format参数中可能用到的格式化串: %(name)s --------Logger的名字 %(levelno)s -----数字形式的日志级别 %(levelname)s ---文本形式的日志级别 %(pathname)s ----调用日志输出函数的模块的完整路径名,可能没有 %(filename)s ----调用日志输出函数的模块的文件名 %(module)s ------调用日志输出函数的模块名 %(funcName)s ----调用日志输出函数的函数名 %(lineno)d ------调用日志输出函数的语句所在的代码行 %(created)f------当前时间,用UNIX标准的表示时间的浮 点数表示 %(relativeCreated)d ---输出日志信息时的,自Logger创建以 来的毫秒数 %(asctime)s -----字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒 %(thread)d ------线程ID。可能没有 %(threadName)s --线程名。可能没有 %(process)d -----进程ID。可能没有 %(message)s -----用户输出的消息
logger对象配置流程
import logging logger = logging.getLogger() # 设置日志级别 logger.setLevel(logging.DEBUG) # 创建一个handler(或者多个),用于写入日志文件 fh = logging.FileHandler('log', encoding='utf-8') # 创建一个handler,用于输出到控制台 sh = logging.StreamHandler() # 输出格式,可定义多个 formatter = logging.Formatter('%(asctime)s - %(module)s/%(filename)s[line:%(lineno)d] - %(levelname)s:%(message)s') # handler 关联 输出格式 fh.setFormatter(formatter) sh.setFormatter(formatter) # logger对象可以添加多个fh和sh对象 logger.addHandler(fh) logger.addHandler(sh) # 写日志,括号内可写任意内容 logger.debug('排错信息') logger.info('正常信息') logger.warning('警告信息') logger.error('错误信息') logger.critical('严重错误信息')
logging库提供了多个组件:Logger、Handler、Filter、Formatter。Logger对象提供应用程序可直接使用的接口;Handler发送日志到适当的目的地;Filter提供了过滤日志信息的方法;Formatter指定日志显示格式;另外,可以通过logger.setLevel(logging.Debug)设置级别。