python入门_模块2

0.collections模块

在内置数据类型(dict、list、set、tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter、deque、defaultdict、namedtuple和OrderedDict等。

1.namedtuple: 生成可以使用名字来访问元素内容的tuple

2.deque: 双端队列,可以快速的从另外一侧追加和推出对象

3.Counter: 计数器,主要用来计数

4.OrderedDict: 有序字典

5.defaultdict: 带有默认值的字典

# namedtuple 具名元组
# tuple可以表示不变集合,例如,一个点的二维坐标就可以表示成:
# 例如:
from collections import namedtuple
a = (1,2)
p = namedtuple('坐标',['x','y'])
location1 = p(1,2)  # 元素的个数一定要跟上面第二个参数的个数相同
print(location1)   # 坐标(x=1, y=2)
print(location1.x)  # 1
print(location1.y)  # 2

card = namedtuple('扑克牌',['color','number'])
A = card('','A')  
print(A)              # 扑克牌(color='♠', number='A')
print(A.color)     #
print(A.number)  # A
# 用具名元组来记录一个城市的信息
from collections import namedtuple
City = namedtuple('City', 'name country population coordinates')
bj = City('BeiJing', 'BJ', 39.9, (116.2317, 39.5427))
print(bj)
# City(name='BeiJing', country='BJ', population=39.9, coordinates=(116.2317, 39.5427))
print(bj.population)
# 39.9

deque双端队列

# 使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低。

# deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:
from collections import deque

q = deque(['s','n','v'])
# append 和appendleft 分别向2端添加元素
q.append('1')
q.appendleft('2')
print(q) # deque(['2', 's', 'n', 'v', '1'])
# pop与popleft分别是2端删除元素
q.pop()
q.popleft()
print(q) # deque(['s', 'n', 'v'])

Counter计数器

# Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key,其计数作为value

from collections import Counter

c = Counter('asdfadghfd_asdf 31242431!')
print(c)
# Counter({'d': 4, 'a': 3, 'f': 3, 's': 2, '3': 2, '1': 2, '2': 2, '4': 2, 'g': 1, 'h': 1, '_': 1, ' ': 1, '!': 1})

OrderedDict: 有序字典

from collections import OrderedDict
d = dict([('a',1),('c',2),('b',3),('e',0)])
print(d)
# {'a': 1, 'c': 2, 'b': 3, 'e': 0}
d['z'] = 5
d['y'] = 6
print(d)
# {'a': 1, 'c': 2, 'b': 3, 'e': 0, 'z': 5, 'y': 6}
print(d.keys())
# dict_keys(['a', 'c', 'b', 'e', 'z', 'y'])

defaultdict: 带有默认值的字典

from collections import defaultdict

# 有下列集合,将所有大于66的值保存至字典的第一个key中,将小于66的值保存在第二个key值中
# 即: {'k1': 大于66 , 'k2': 小于66}
ls = [11,22,33,44,55,66,77,88,99]
my_dict= defaultdict(list)
for i in ls:
    if i>66:
        my_dict['k1'].append(i)
    else:
        my_dict['k2'].append(i)

print(my_dict())
# defaultdict(<class 'list'>, {'k2': [11, 22, 33, 44, 55, 66], 'k1': [77, 88, 99]})

# 
from collections import defaultdict
dd = defaultdict(lambda: 'N/A')
dd['key1'] = 'abc'
dd['key1'] # key1存在
'abc'
dd['key2'] # key2不存在,返回默认值
'N/A'

一、random模块

(0, 1):random.random()
[1, 10]:random.randint(1, 10)
[1, 10):random.randrange(1, 10)
(1, 10):random.uniform(1, 10)
单例集合随机选择1个:random.choice(item)
单例集合随机选择n个:random.sample(item, n)
洗牌单列集合:random.shuffle(item)

import random
#模块地址
print(random) # <module 'random' from 'F:\\安装包\\Python36\\lib\\random.py'>

#random.random()取值范围0-1之间的小数,不包含0和1
for i in range(2):
    print(random.random())  #(0,1)
#取值0-2之间,但是random.random()输出的值都是0-1之间
#0.6179348878875671
#0.7546476092920288


#random.randint()  取值范围[1,5]:包含1和5
for i in range(5):
    print(random.randint(1,5))  #[1,5]

# random.randrange()  #[1,5)取值范围1-4,不包含5
for i in range(5):
    print(random.randrange(1,5))  #[1,4]

# random.uniform()  取值范围(1,5)之间的小数
for i in range(5):
    print(random.uniform(1,5))  #小数:(1,5)
#     print('%.3f' % random.uniform(1,5))  #%f 可以定义取值的长度 %.3f取3位小数

#洗牌单列集合:random.shuffle(item)
ls = [1,2,3,4,5]
print(random.shuffle(ls)) #None
print(ls) #对ls随机排序[4, 5, 2, 3, 1]

#单例集合随机选择1个:random.choice(item)
print(random.choice(ls))  # 随机输出ls中一个值

#单例集合随机选择n个:random.sample(item, n)
print(random.sample(ls,3))  # [2, 4, 3]  不会同时出现2个一样的数字
练习:验证码功能
# 方法一
# count验证码位数,根据需求出几位
import random
def get_code(count):
    code = ""
    # 能产生大小写字母和数字
    # 进行字符串拼接
    for i in range(count):
        c1 = chr(random.randint(65,90))  #(65,90)  ascii表对应大写字母,chr对应数字转换为字母
        c2 = chr(random.randint(97,122)) #(97,122) ascii表对应小写字母
        c3 = str(random.randint(0,9))    # 数字

        code += random.choice([c1,c2,c3])
    return code

print(get_code(4))

# 方法二:效率高,只需要循环2个,但逻辑多
def get_code(count):
    code = ""
    for i in range(count):
        r = random.choice([1,2 ,3])
        if r == 1:
            c = chr(random.randint(65,90))
        elif r == 2:
            c = chr(random.randint(97,122))
        else:
            c = str(random.randint(0,9))
        code += c
    return code

print(get_code(6)) # d7H7C3

# 第3中方法:将所有字母数字的选项都写在一起,每次选中都不会重复
def get_code(count):
    target = "1234567890QWERTYUIOPASDFGHJKLZXCVBNMwqertiuopadsfklzvcxbnm"
    code_list = random.sample(target,count)
    return ''.join(code_list) #3PqXiR82
    return code_list  #['p', '1', 'S', '9', 'c', 's', 'k', 'A']
print(get_code(18))  #Kv47ZiznkPD0eO3I5d

二、序列化模块

# 什么是序列化:将对象转化为字符串
# 什么是反序列化:将字符串转化为对象
# 为什么要序列化:数据的存储和传输都采用的是字符串类型
# 序列化的模块:json pickle shelve

# json:支持跨语言,用于数据的传输
# pickle:支持py的所有数据类型,所有可以将所有py的对象序列化后存储
# shelve:支持py的所有数据类型,可以即时存与取

# 序列化
dump
dumps

# 反序列化
load
loads

三.Json模块:用于传输(多语言支持)

什么是json:就是完成文本序列化得到的文本字符串,json字符串具有一定的语法规范

1.支持的数据类型:int float str bool dict list null   # json中布尔类型转换为小写,不支持set 和tuple
2.复杂的json都是由{}与[]嵌套形成的数据
3.json字符串只能有一个根: json_str = '{}{}' | '{}[]' | '[][]' | '1null'  # 报错,都是两个根
4.json中的str类型必须用""包裹(json字符串中的字符串类型不支持'' """""")
import json

# python对象 序列化 json字符串
data = None
res = json.dumps(data)
print(res)

# json字符串 反序列化 python对象
json_str = '3.14'
json_str = 'true'
json_str = 'null'
json_str = '{}'
json_str = '[]'
json_str = '1, null'  # 有误,两个根

json_str = "\"abc\""  #反序列化去引号
json_str = '"abc"'
obj = json.loads(json_str)
print(obj, type(obj))
操作文件:读()存与写(取)
#
序列化 obj = {'name': 'Simon', 'age': 17, 'gender': ''} with open('a.txt', 'w', encoding='utf-8') as wf: json.dump(obj, wf, ensure_ascii=False) # ensure_ascii 默认为True ansci码;#False跟随文件编码utf-8 # json.dump(obj, wf) #{"name": "Simon", "age": 17, "gender": "男"}{"name": "Simon", "age": 17, "gender": "\u7537"} # 文件内容123456:因为wf文件只打开一次,打开的时候操作一次,之后都是写 # wf.write('123') # wf.write('456') # 反序列化 with open('a.txt', 'r', encoding='utf-8') as rf: obj = json.load(rf) print(obj) # {'name': 'Simon', 'age': 17, 'gender': '男'} # 注:json模块的序列化与反序列化是一一对应关系 print(json.load(open('a.txt', 'r', encoding='utf-8'))) # {'name': 'Simon', 'age': 17, 'gender': '男'}

四、 pickle模块:支持所有数据类型(不支持其他语言,只用于python)

import pickle
obj = {'name': 'simon', 'age': 17, 'gender': ''}
res = pickle.dumps(obj)
print(res)
# b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x05\x00\x00\x00simonq\x02X\x03\x00\x00\x00ageq\x03K\x11X\x06\x00\x00\x00genderq\x04X\x03\x00\x00\x00\xe7\x94\xb7q\x05u.'

pickle.dump(obj,open('b.txt','wb'))
#内容: �}q (X   nameqX   simonqX   ageqKX   genderqX   男qu.

# 反序列化
print(pickle.loads(res)) # {'name': 'simon', 'age': 17, 'gender': '男'}
print(pickle.load(open('b.txt', 'rb'))) # {'name': 'simon', 'age': 17, 'gender': '男'}

五、shelve模块:支持所有数据类型(优化存与取的方式)

# shelve将dump与load封装为一步
import shelve
# 将文件的方法封装到模块中,文件后缀自己随便取的
#新建了3个文件:c.shv.bak c.shv.dat c.shv.dir
shv_tool = shelve.open('c.shv')

# 序列化
# shv_tool['name'] = 'Simon'

# 反序列化
res = shv_tool['name']
print(res) # Simon

shv_tool.close()
# 文件通过shelve对象来关闭,关闭后就不能再取,否则就报错

#  二次操作:重新打开与关闭
shv_tool = shelve.open('c.shv')
print(shv_tool['name']) # Simon
shv_tool.close()

# 操作方式
with shelve.open('c.shv') as shv_tool:
    print(shv_tool['name']) # Simon

# writeback将反序列化到内存的数据,操作后即时同步到文件中
with shelve.open('c.shv', writeback=True) as shv_tool:
    shv_tool['stus'] = ['Bob', 'Tom']  # 重置
    # print(shv_tool['stus'])  # ['Bob', 'Tom']

    shv_tool['stus'].append('Jobs')  #  将数据取到内存,在内存中添加
    print(shv_tool['stus']) # ['Bob', 'Tom', 'Jobs']

六、shutil:可以操作权限的处理文件模块

import shutil

# 基于路径的文件复制:
shutil.copyfile('source_file', 'target_file')
# 绝对路径:复制后的文件名是需要的
shutil.copyfile(r'C:\shelev.py',r'D:\target.py')

# 基于流的文件复制:
with open('source_file', 'rb') as r, open('target_file', 'wb') as w:
    shutil.copyfileobj(r, w)
    
# 递归删除目标目录:目录里有文件也直接删除
shutil.rmtree('target_folder')

# 文件移动
shutil.remove('old_file', 'new_file')

# 文件夹压缩
# file_name: 压缩后得到的文件名  format:压缩格式  archive_path:要压缩的文件夹路径
shutil.make_archive('file_name', 'format', 'archive_path')
# 举例
shutil.make_archive('target/abc', 'zip', 'source') #在target 目录中将source目录压缩到target目录中名为abc.zip

# 文件夹解压
# unpack_file: 解压的文件  unpack_name:解压得到的文件夹名  format:解压格式
shutil.unpack_archive('unpack_file', 'unpack_name', 'format')
# 举例
shutil.unpack_archive('target/abc.zip', 'target/xyz', 'zip') #将abc.zip解压为xyz文件夹

 七、加密模块

# 一般加密解密方法:
# md5加密:不可逆加密
# 碰撞解密:用数据再进行一次加密,与原加密结果做匹配

hashlib模块加密

import hashlib

data = '数据'  # 生成对象
lock_obj = hashlib.md5(data.encode('utf-8'))  # 生产加密锁对象,传入加密数据
result = lock_obj.hexdigest()  # 获取加密后的加密串
print(result)

# update可以往锁对象中添加加密数据
lock_obj = hashlib.md5()
lock_obj.update(b'123')
lock_obj.update(b'abc')
lock_obj.update('嘿嘿'.encode('utf-8'))
print(lock_obj.hexdigest())

lock_obj.update(b'000')
print(lock_obj.hexdigest())  # 000 | '123abc嘿嘿000'.encode('utf-8')

print(hashlib.md5('123abc嘿嘿000'.encode('utf-8')).hexdigest())
# 注:要为新数据提供加密,一定要为该数据创建一个加密对象
# 加盐:前提是支持update
# 什么是加盐:在原数据前或后添加一些预定的数据,与原数据一起进行加密
# 为什么要加盐:
# 1.当原数据过于简单,可以对其加盐,提高数据的复杂度
# 2.盐与数据有一定相似度,混淆对真实数据的提取

data = 'ab_12'
lock_obj = hashlib.md5()
lock_obj.update(b'a12_d')
lock_obj.update(data.encode('utf-8'))
lock_obj.update(b'dd_121')
print(lock_obj.hexdigest())
# a12_dab_12dd_121在数据前后加盐混淆

# 其他位数加密
lock_obj = hashlib.sha3_256(b'123')
print(lock_obj.hexdigest())
lock_obj = hashlib.sha3_512(b'123')
lock_obj.update(b'salt')
print(lock_obj.hexdigest())

hmac模块加密

import hmac
# 与hashlib的不同点:生产锁对象时必须提高数据参数
lock_obj = hmac.new(b'')
print(lock_obj.hexdigest())

# 支持加盐 lock_obj
= hmac.new(b'') lock_obj.update(b'salt') print(lock_obj.hexdigest())

 八、logging:日志模块

# logging记录项目日志的模块
# 记录日志:将项目中产生的一些数据,或是信息,或是错误不再输出到控制台,而是输出到文件中,保存这样信息的文件就称之为日志文件
# 日志级别
在开始记录日志前还需要明确,日志的级别

随着时间的推移,日志记录会非常多,成千上万行,如何快速找到需要的日志记录这就成了问题

解决的方案就是 给日志划分级别
logging模块将日志分为了五个级别,从高到低分别是:
1.info 常规信息
2.debug 调试信息
3.warning 警告信息(默认级别)
4.error 错误信息
5.cretical 严重错误

本质上他们使用数字来表示级别的,从高到低分别是10,20,30,40,50
#1.导入模块
import logging

#2.输出日志
logging.info("info")
logging.debug("debug调试")
logging.warning("warning警告")
logging.error("error错误")
logging.critical("critical严重错误")

# 输出 WARNING:root:warning
# 输出 ERROR:root:error
# 输出 CRITICAL:root:critical

1.logging模块的基本配置使用:

import logging
import sys

# 2.日志的基本配置
logging.basicConfig(
    # 输出级别
    level=logging.INFO,
    # level=10,

    # 输出位置
    # stream=sys.stderr,  # sys.stdout  往控制台输出
    filename='log/my.log',  # 往文件输出  => 如果需要同时往多个位置输出,需要handles

    # 输出格式
    format='%(asctime)s[%(name)s]: %(msg)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)
# my.log日志:2019-05-16 14:39:32[root]: error msg
# 标准输入 # print(sys.stdin.readline())

# filename:用指定的文件名创建FiledHandler(后边会具体讲解handler的概念),这样日志会被存储在指定的文件中。
# filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
# format:指定handler使用的日志显示格式。 
# datefmt:指定日期时间格式。 
# level:设置rootlogger(后边会讲解具体概念)的日志级别 

#案例:
logging.basicConfig(
    filename="aaa.log",
    filemode="at",
    datefmt="%Y-%m-%d %H:%M:%S %p",
    format="%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s",
    level=10
)

格式化全部可用名称:

%(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:用户输出的消息

2.logging的成员组成

 

logging模块的四个核心角色:
1.Logger   日志生成器 产生日志
2.Filter    日志过滤器  过滤日志
3.Handler 日志处理器 对日志进行格式化,并输出到指定位置(控制台或文件)
4.Formater 处理日志的格式
import logging

# 1.打印者:自定义的打印者如何配置
log1 = logging.getLogger('logger name')

# 2.输出位置:两个文件输出位置与一个控制台输出位置
hd_a = logging.FileHandler('log/a.log', encoding='utf-8')
hd_cmd = logging.StreamHandler()

# 3.输出格式
fmt1 = logging.Formatter('%(asctime)s 【%(name)s】- %(msg)s')
fmt2 = logging.Formatter('%(asctime)s - %(msg)s')

# 4.打印者添加句柄 - 设置打印者的输出位置
log1.addHandler(hd_a)
log1.addHandler(hd_cmd)

# 5.将格式绑定给输出位置(句柄)
hd_a.setFormatter(fmt1)
hd_cmd.setFormatter(fmt2)

# 6.权限控制
log1.setLevel(logging.DEBUG)  # 打印者规定打印级别
hd_a.setLevel(logging.WARNING)  # 不同输出位置(句柄)再可以二次限定输出级别
hd_cmd.setLevel(logging.DEBUG)  # 不同输出位置(句柄)再可以二次限定输出级别

# 7.不同级别输出信息
log1.debug('debug msg')
log1.info('info msg')
log1.warning('warning msg')
log1.error('error msg')
log1.critical('critical msg')
import logging
# root打印者,用logging.basicConfig来配置
# logging.critical('12345')

# 1.打印者:自定义的打印者如何配置
log1 = logging.getLogger('Owen')
# log1.critical('67890')

log2 = logging.getLogger('Simon')
# log2.critical('00000')

# 2.输出位置:两个文件输出位置与一个控制台输出位置
hd_a = logging.FileHandler('log/a.log', encoding='utf-8')
hd_b = logging.FileHandler('log/b.log', encoding='utf-8')
hd_cmd = logging.StreamHandler()  #控制台输出

# )) 为输出者绑定输出位置
log1.addHandler(hd_a)
log1.addHandler(hd_b)

log2.addHandler(hd_b)
log2.addHandler(hd_cmd)

# 3.输出格式
fmt1 = logging.Formatter('%(asctime)s 【%(name)s】- %(msg)s')
fmt2 = logging.Formatter('%(asctime)s - %(msg)s')

# ))将格式绑定给输出位置(句柄)
hd_a.setFormatter(fmt1)
hd_b.setFormatter(fmt1)
hd_cmd.setFormatter(fmt2)

# 级别控制: 打印者规定打印级别,输出位置(句柄)再可以二次限定,级别>=打印者级别
log2.setLevel(logging.DEBUG)
hd_b.setLevel(logging.WARNING)
hd_cmd.setLevel(logging.DEBUG)

log2.debug('debug msg')
log2.info('info msg')
log2.warning('warning msg')
log2.error('error msg')
log2.critical('critical msg')

# 4.输出
log1.critical('log1 输出的 critical msg')
log2.critical('log2 输出的 critical msg')

# 过滤:少用
# logging.Filter
多日志多级别输出

 3.logging配置文件项目开发运用

# 1.将打印者,句柄,与格式封装成配置信息
# 2.加载配置信息
# 3.使用自定义logger,采用的就是配置信息设置的logger

# 优势:1,2两步是一劳永逸的,后期开发只需要在要记录日志的文件中使用自定义logger
# 一、基础配置:目录conf/setting.py
LOGGING_DIC = {
    'version': 1,
    'disable_existing_loggers': False, #让系统的默认配置失效
    'formatters': {
        # 名称格式定义
        'o_fmt1': {
            'format': '%(asctime)s 【%(name)s】- %(msg)s'
        },
        'o_fmt2': {
            'format': '%(asctime)s - %(msg)s'
        }
    },
    'filters': {},
    'handlers': {
        'o_hd_file': {
            'level': 'WARNING',
            'class': 'logging.handlers.RotatingFileHandler',  # 打印到控制台
            'formatter': 'o_fmt1',
            'filename': 'log/sys.log',
            'encoding': 'utf-8',
            'maxBytes': 1024*1024*5,  # 日志大小5M
            'backupCount': 5,   # 文件切分:写满一个就改名为sys1.log,写满5个清空重新记录
        },
        'o_hd_cmd': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',  # 打印到控制台
            'formatter': 'o_fmt2'
        }
    },
    'loggers': {
        'o_owen': {
            'level': 'DEBUG',
            'handlers': ['o_hd_file', 'o_hd_cmd']
        },
        'o_simon': {
            'level': 'DEBUG',
            'handlers': ['o_hd_cmd'],
            # 'propagate': True  # 向更高的level logging传递
        }
    }
}

# 二、加载配置
import logging.config
logging.config.dictConfig(LOGGING_DIC)


# 三、使用
log = logging.getLogger('o_simon')
log.critical('信息')

log1 = logging.getLogger('o_owen')
log1.critical('信息')

# 目录:lib/common.py
from conf.settings import LOGGING_DIC
import logging.config
logging.config.dictConfig(LOGGING_DIC)

def getLogger(name):
    return logging.getLogger(name)
测试:
from lib.common import getLogger
log = getLogger('o_owen')

log.debug('12345')
log.critical('67890')
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_path = "配置文件路径"

LOGGING_DIC = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': standard_format
        },
        'simple': {
            'format': simple_format
        },
    },
    'filters': {},
    'handlers': {
        #打印到终端的日志
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',  # 打印到屏幕
            'formatter': 'simple'
        },
        #打印到文件的日志,收集info及以上的日志
        'default': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
            'formatter': 'standard',
            'filename': logfile_path,  # 日志文件
            'maxBytes': 1024*1024*5,  # 日志大小 5M
            'backupCount': 5, #日志文件最大个数
            'encoding': 'utf-8',  # 日志文件的编码
        },
    },
    'loggers': {
        #logging.getLogger(__name__)拿到的logger配置
        'aa': {
            'handlers': ['default', 'console'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)传递
        },
        # 把key设置为空
        '': {
            'handlers': ['default', 'console'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)传递
        },
    },
}
标准代码
#lib/common.py
#日志功能
import logging.config
from conf import settings

# 在lib 文件夹的common文件中生成日志对象
#生成日志对象
def get_logger(name):
    # 先把日志配置传给logging
    logging.config.dictConfig(settings.LOGGING_DIC)
    # 生产日志对象--》接收的是name,根据name打印相应的日志
    my_logger = logging.getLogger(name)
    return my_logger


# conf/settings.py
import os

# test目录
BASE_PATH = os.path.dirname(os.path.dirname(__file__))
# test/db目录
DB_PATH = os.path.join(BASE_PATH, 'db')


# 日志配置文件
'''
logging 配置
'''
import os
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'

# 日志目录
LOG_PATH = os.path.join(BASE_PATH,'log')
log_filename = 'Atm_Shop.log'

if not os.path.isdir(LOG_PATH):
    os.mkdir(LOG_PATH)

# log文件全路径
logfile_path = os.path.join(LOG_PATH,log_filename)


LOGGING_DIC = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': standard_format
        },
        'simple': {
            'format': simple_format
        },
    },
    'filters': {},
    'handlers': {
        #打印到终端的日志
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',  # 打印到屏幕
            'formatter': 'simple'
        },
        #打印到文件的日志,收集info及以上的日志
        'default': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
            'formatter': 'standard',
            'filename': logfile_path,  # 日志文件
            'maxBytes': 1024*1024*5,  # 日志大小 5M
            'backupCount': 5, #日志文件最大个数
            'encoding': 'utf-8',  # 日志文件的编码
        },
    },
    'loggers': {
        #logging.getLogger(__name__)拿到的logger配置
        'aa': {
            'handlers': ['default', 'console'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)传递
        },
        # 把key设置为空
        '': {
            'handlers': ['default', 'console'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)传递
        },
    },
}


# 引用
from lib import common
# 获取银行日志功能
bank_log = common.get_logger('bank')

bank_log.info('%s提现%s元成功,手续费为%s' % (user, money2,money3))
标准代码2

九、re模块

# re:正则,全称正则字符串 - re就是有特殊语法的字符串
# re可以将有正则语法的字符串解析为对应的正则对象,用来匹配目标字符串

# 学习re的目的:1.判断目标字符串是否合法 2.在目标字符串中提取想要的信息(信息匹配规则采用正则)
import re

# 从123abc123中查找1
r1 = re.findall(r'1', '123abc123')
print(r1) # ['1', '1']

r2 = re.findall(r'a', '123abc123ABC', flags=re.I) # re.I 不区分大小写匹配
print(r2) # ['a', 'A']

# 1.将 r'\d' 丢给_compile得到可以匹配数字的 正则对象
# 2.正则对象.findall('目标字符串')
r3 = re.findall(r'\d', '123abc123')
print(r3) # ['1', '2', '3', '1', '2', '3']

re_obj = re.compile(r'\d')  # 将 r'\d' 丢给_compile得到可以匹配数字的 正则对象
r4 = re_obj.findall('123abc123')  # 正则对象.findall('目标字符串')
print(r4) # ['1', '2', '3', '1', '2', '3']

正则语法

单个字符语法:

import re

# 一、单个字符语法
# 匹配a
print(re.findall(r'a', '123abc嘿嘿'))  # ['a']

# a或b
print(re.findall(r'a|b', '123abc嘿嘿'))  # ['a', 'b'] 不建议使用
print(re.findall(r'[ab]', '123abc嘿嘿'))  # ['a', 'b'] 建议使用

# 非a非b
print(re.findall(r'[^ab]', '123abc嘿嘿'))  # ['1', '2', '3', 'c', '嘿', '嘿']

# 数字
print(re.findall(r'[0-9]', '12abc嘿嘿12'))  # ['1', '2', '1', '2'] 建议使用
print(re.findall(r'\d', '12abc嘿嘿12'))  # ['1', '2', '1', '2'] 不建议使用

# 字母
print(re.findall(r'[a-zA-Z]', '12abc[嘿嘿ABC'))  # ['a', 'b', 'c', 'A', 'B', 'C']

# 字母数字_常用汉字:\w => 建议使用 [a-zA-Z0-9_]
print(re.findall(r'\w', '12abc[_嘿嘿ABC'))  # ['1', '2', 'a', 'b', 'c', '_', '嘿', '嘿', 'A', 'B', 'C']

# 汉字 [\u4e00-\u9fa5]代表汉字
print(re.findall(r'[\u4e00-\u9fa5]', '12abc[_嘿嘿ABC'))  # ['嘿', '嘿']

# 空白字符:\s => 建议使用[ \f\n\r\t\v]
print(re.findall(r'\s', ' \f\n\r\t\v'))  # [' ', '\x0c', '\n', '\r', '\t', '\x0b']

# 非\n的任意字符: .
print(re.findall(r'.', ' \f\n\r\t\v*&_.'))  # [' ', '\x0c', '\r', '\t', '\x0b', '*', '&', '_', '.']

# 只想匹配.字符:\.
print(re.findall(r'\.', ' \f\n\r\t\v*&_.'))  # ['.']

# re.S: 让.也能匹配\n,就可以理解为 . 可以匹配所有字符
print(re.findall(r'.', ' \f\n\r\t\v*&_.', flags=re.S))

# 取对立面 \d数字 \D非数字  \w=>\W  \s=>\S
print(re.findall(r'\D', '12abc\f嘿嘿12'))  # ['a', 'b', 'c', '\x0c', '嘿', '嘿']

 重复字符语法:

print(re.findall(r'ab', 'abacbabc'))  # ['ab', 'ab']

# 指定个数: 匹配abb
print(re.findall(r'ab{2}', 'aababbabbb'))  # ['abb', 'abb']

# 贪婪匹配: 尽可能多的匹配
# a0~2个b: a | ab | abb
print(re.findall(r'ab{,2}', 'aababbabbb'))  # ['a', 'ab', 'abb', 'abb']

# a0~n个b:
print(re.findall(r'ab{0,}', 'aababbabbb'))  # ['a', 'ab', 'abb', 'abbb']

# a1~3个b:
print(re.findall(r'ab{1,3}', 'aababbabbb'))  # ['ab', 'abb', 'abbb']

# *: {0,}
print(re.findall(r'ab*', 'aababbabbb'))  # ['a', 'ab', 'abb', 'abbb']
# +: {1,}
print(re.findall(r'ab+', 'aababbabbb'))  # ['ab', 'abb', 'abbb']
# ?: {,1}
print(re.findall(r'ab?', 'aababbabbb'))  # ['a', 'ab', 'ab', 'ab']

# 非贪婪匹配
print(re.findall(r'ab{1,3}?', 'aababbabbb'))  # ['ab', 'ab', 'ab']

# 重点:非贪婪匹配应用场景,一般都是结合有开头与结尾的标识
print(re.findall(r'<.{1,}>', '<a><b>msg</b></a>'))  # ['<a><b>msg</b></a>']
# 匹配标签
print(re.findall(r'<.{1,}?>', '<a><b>msg</b></a>'))  # ['<a>', '<b>', '</b>', '</a>']

# *?: {0,}?
# +?: {1,}?
# ??: {,1}?
print(re.findall(r'<.+?>', '<a><b>msg</b></a>'))  # ['<a>', '<b>', '</b>', '</a>']

 分组语法:

# 引子
print(re.findall(r'(?:ab){2}', 'abbabab'))  # ['abab']

# findall(): 没有分组情况下,显示匹配的结果;如果有分组,显示分组结果

# 分组:()
# 取消分组:(?:)
# 有名分组:(?P<名字>)

# 案例:
# 匹配链接
print(re.findall(r'www\..+?\.com', 'www.baidu.comabcwww.sina.com'))  # ['www.baidu.com', 'www.sina.com']
# 获取链接的域名:['baidu', 'sina']
print(re.findall(r'www\.(.+?)\.com', 'www.baidu.comabcwww.sina.com'))  # ['baidu', 'sina']

# 分组编号: 从左往右数左(进行分组编号
# [('www.baidu.com', 'baidu', 'com'), ('www.sina.edu', 'sina', 'edu')]
res = re.findall(r'(www\.(.+?)\.(com|edu))', 'www.baidu.comabcwww.sina.edu')
print(res)
print(res[0][1])

# 取消分组:(?:) 应用于,要将一些数据作为整体看待,但由不能产生分组
# [('www.baidu.com', 'baidu'), ('www.sina.edu', 'sina')]
res = re.findall(r'(www\.(.+?)\.(?:com|edu))', 'www.baidu.comabcwww.sina.edu')
print(res)

其他正则方法的使用:

# match:不是全文匹配,必须从头开始匹配,且只匹配一次
res = re.match(r'(www\.(?P<site_name>.+?)\.(?:com|edu))', 'www.baidu.comwww.sina.edu')
# 可以通过分组号直接取出分组内容
print(res.group(1))
print(res.group(2))
# print(res.group(0), res)  # 匹配的整体

# 有名分组
print(res.group('site_name'))

# split(): 拆分
print('abc def xyz'.split(' '))
print(re.split(r' ', 'abc def xyz'))
print(re.split(r'[,@ ]', 'abc,def@xyz opq'))


# sub(): 替换
res = re.sub(r'good', 'bed', 'good good day a')
print(res)  # bed bed day a

res = re.sub(r'good', 'bed', 'good good day a', count=1)
print(res)  # bed good day a

res = re.sub(r'good day a', '123', 'good day a!!!')
print(res)  # 123!!!

# 结合分组可以完成数据的重组
res = re.sub(r'(good) (day) (a)', r'today is \3 \1 \2', 'good day a!!!')
print(res)  # today is a good day!!!
元字符描述
\ 将下一个字符标记符、或一个向后引用、或一个八进制转义符。例如,“\n”匹配\n。“\n”匹配换行符。序列“\”匹配“\”而“(”则匹配“(”。即相当于多种编程语言中都有的“转义字符”的概念。
^ 匹配输入字行首。如果设置了RegExp对象的Multiline属性,^也匹配“\n”或“\r”之后的位置。
$ 匹配输入行尾。如果设置了RegExp对象的Multiline属性,$也匹配“\n”或“\r”之前的位置。
* 匹配前面的子表达式任意次。例如,zo能匹配“z”,也能匹配“zo”以及“zoo”。等价于{0,}。
+ 匹配前面的子表达式一次或多次(大于等于1次)。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}。
{n} n是一个非负整数。匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o。
{n,} n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”。
{n,m} mn均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o为一组,后三个o为一组。“o{0,1}”等价于“o?”。请注意在逗号和两个数之间不能有空格。
? 匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“do”或“does”。?等价于{0,1}。
? 当该字符紧跟在任何一个其他限制符(,+,?,{n},{n,},{n,m*})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少地匹配所搜索的字符串,而默认的贪婪模式则尽可能多地匹配所搜索的字符串。例如,对于字符串“oooo”,“o+”将尽可能多地匹配“o”,得到结果[“oooo”],而“o+?”将尽可能少地匹配“o”,得到结果 ['o', 'o', 'o', 'o']
.点 匹配除“\n”和"\r"之外的任何单个字符。要匹配包括“\n”和"\r"在内的任何字符,请使用像“[\s\S]”的模式。
   
x|y 匹配x或y。例如,“z|food”能匹配“z”或“food”(此处请谨慎)。“[zf]ood”则匹配“zood”或“food”。
[xyz] 字符集合。匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”。
[^xyz] 负值字符集合。匹配未包含的任意字符。例如,“abc”可以匹配“plain”中的“plin”任一字符。
[a-z] 字符范围。匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”范围内的任意小写字母字符。注意:只有连字符在字符组内部时,并且出现在两个字符之间时,才能表示字符的范围; 如果出字符组的开头,则只能表示连字符本身.
[^a-z] 负值字符范围。匹配任何不在指定范围内的任意字符。例如,“a-z”可以匹配任何不在“a”到“z”范围内的任意字符。
\b 匹配一个单词的边界,也就是指单词和空格间的位置(即正则表达式的“匹配”有两种概念,一种是匹配字符,一种是匹配位置,这里的\b就是匹配位置的)。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”;“\b1”可以匹配“1_23”中的“1”,但不能匹配“21_3”中的“1_”。
\B 匹配非单词边界。“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”
\s 匹配任何不可见字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。
\S 匹配任何可见字符。等价于 \f\n\r\t\v
\w 匹配包括下划线的任何单词字符。类似但不等价于“[A-Za-z0-9_]”,这里的"单词"字符使用Unicode字符集。
\W 匹配任何非单词字符。等价于“A-Za-z0-9_”。
\d 匹配一个数字字符。等价于[0-9]。grep 要加上-P,perl正则支持
\D 匹配一个非数字字符。等价于0-9。grep要加上-P,perl正则支持
\n 匹配一个换行符。等价于\x0a和\cJ。
\r 匹配一个回车符。等价于\x0d和\cM。
\t 匹配一个制表符。等价于\x09和\cI。
( ) 将( 和 ) 之间的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用 \1 到\9 的符号来引用。
(?:pattern) 非获取匹配,匹配pattern但不获取匹配结果,不进行存储供以后使用。这在使用或字符“(|)”来组合一个模式的各个部分时很有用。例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式。
| 将两个匹配条件进行逻辑“或”(Or)运算。例如正则表达式(him|her) 匹配"it belongs to him"和"it belongs to her",但是不能匹配"it belongs to them."。注意:这个元字符不是所有的软件都支持的。

十、shell脚本之subprocess模块

#Popen
order = subprocess.Popen('dir',
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)

res = order.stdout.read().decode('gbk')
print(res)

print('==============================')

res = order.stderr.read().decode('gbk')
print(res)
# 举例
# windows下测试列出目录文件
# run
import subprocess
order = subprocess.run('dir',
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)

res = order.stdout.decode('gbk')
print(res)

print('==============================')
# stderr 输出错误
res = order.stderr.decode('gbk')
print(res)
#Centos系统
>>> a = subprocess.call(['df','-lh'],shell=False)
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        40G  9.0G   29G  25% /
/dev/vdb         99G   16G   78G  18% /db
tmpfs           783M     0  783M   0% /run/user/1000

>>> r1=subprocess.getstatusoutput("dir")
>>> r1
(0, 'lova  test.sh')

>>> r2 = subprocess.Popen('ping -c2 www.baidu.com',shell=True)
>>> PING www.a.shifen.com (115.239.210.27) 56(84) bytes of data.
64 bytes from 115.239.210.27 (115.239.210.27): icmp_seq=1 ttl=54 time=8.59 ms
64 bytes from 115.239.210.27 (115.239.210.27): icmp_seq=2 ttl=54 time=8.64 ms

--- www.a.shifen.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 8.595/8.621/8.648/0.094 ms

>>> subprocess.Popen('./test.sh',shell=True)
<subprocess.Popen object at 0x7f2c51f10160>
>>> Hello World!

>>> res = subprocess.Popen('./test.sh',stdout = subprocess.PIPE,shell=True,stderr=subprocess.PIPE)
>>> res1 = res.stdout.read()
>>> res1
b'Hello World!\n'
>>> print(res1)
b'Hello World!\n'
def subrun(cmd):
    comply = subprocess.Popen(cmd,
                              shell=True,
                              stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE,
                              )
    res_stdout = comply.stdout.read()
    res_stderr = comply.stderr.read()
    if res_stdout:
        return res_stdout.decode('utf-8')
    return res_stderr.decode('utf-8')
函数传参

十二、configparser 模块

# my.ini 配置文件读写
"""
[server]
ip = 192.168.66.88
port = 3306

[client]
user = root
password = root
"""

import configparser

# 1.初始化
parser = configparser.ConfigParser()

# 2.读
parser.read('my.ini', encoding='utf-8')
# section | option | value
# 读取列表
sts = parser.sections()
print(sts, type(sts))  # ['server', 'client'] <class 'list'>

ops = parser.options(sts[0])
print(ops)  # ['ip', 'port']
value = parser.get(sts[0], ops[0])
print(value, type(value))  # 192.168.66.88 <class 'str'>
# # get=>str getboolean=>bool getfloat=>float getint=>int
print(parser.get('server', 'port')) # 3306

# 3.写
parser.read('my.ini', encoding='utf-8')
parser.set('server', 'port', '6666')
parser.write(open('my.ini', 'w'))

十三、xml模块

# xml文件:1.作为传输文件用于数据的传输  2.作为配置文件配置信息

# 1.只能由一个根标签
# 2.所有的标签都是自定义的
# 3.标签名就是key,标签的内容就是value
# 4.与json不同的是,标签不仅可以有key和value,还有标签的属性
# 注:xml的属性通常用来表示标签间的区分度,用于解析xml来使用
<data>
    <country name="Liechtenstein">
        <rank updated="yes">2</rank>
        <year>2008</year>
        <gdppc>141100</gdppc>
        <neighbor direction="E" name="Austria" />
        <neighbor direction="W" name="Switzerland" />
    </country>
    <country name="Singapore">
        <rank updated="yes">5</rank>
        <year>2011</year>
        <gdppc>88888</gdppc>
        <neighbor direction="N" name="Malaysia" />
    </country>
    <country name="Panama">
        <rank updated="yes">69</rank>
        <year>2011</year>
        <gdppc>13600</gdppc>
        <neighbor direction="W" name="Costa Rica" />
        <neighbor direction="E" name="Colombia" />
    </country>
</data>
my.xml
"""
{"countrys": [
    {
        "rank": 2,
        "year": 2008,
        "gdppc": 141100
    },
    {},
    {}
]}

{
    "data": {
        "countrys": [
            {},
            {},
            {}
        ]
    }
}
"""
# xml的文件解析 => 将xml转化为json类型的数据
# dict list => json

import xml.etree.ElementTree as ET

# 读文件
tree = ET.parse("my.xml")
print(tree)  # xml.etree.ElementTree.ElementTree # <xml.etree.ElementTree.ElementTree object at 0x0000000001EACA58>
# 根节点
root_ele = tree.getroot()
print(root_ele)  # Element 'data':<Element 'data' at 0x0000000001DBD4A8>

# 遍历往下
# print(root_ele[1])
for ele in root_ele:
    print(ele, ele.attrib)
    if ele.attrib['name'] == 'Singapore':
        for e in ele:
            print(e, e.tag)
            if e.tag == 'gdppc':
                print(e.text)
                e.text = '6666'  # 只修改了内容

# 全文搜索指定名的子标签
# ele.iter("标签名")
# 非全文查找满足条件的第一个子标签
# ele.find("标签名")
# 非全文查找满足条件的所有子标签
# ele.findall("标签名")
# print('==============')
# cs = root_ele.iter('country')
# for c in cs:
#     print(c)
# print('==============')
# print(root_ele.find('country').attrib) # {'name': 'Liechtenstein'}
# print(root_ele.find('rank').attrib)  # 不能跨标签取,只能取子标签
# print('==============')
# print(root_ele.findall('country'))

# 将内存的数据重新写入文件
tree.write("my.xml")

# 读
data = {'countrys': []}
tree = ET.parse("my.xml")
root = tree.getroot()
for ele in root:
country = {}
for e in ele:
if e.text and e.text.strip():
country[e.tag] = e.text
data['countrys'].append(country)
print(data)

十四、Excel操作

# Excel
                            年终报表                
              教学部    市场部    咨询部    总计
Jan-19    10    15            5        30
Feb-19    11    20            5        36
Mar-19    12    25            5        42
Apr-19    13    30            5        48
May-19    14    35            5        54
Jun-19    15    40            5        60
Jul-19    16    45            5        66
Aug-19    17    50            5        72
Sep-19    18    55            5        78
Oct-19    19    60            5        84
Nov-19    20    65            5        90
Dec-19    21    70            5        96

基本操作

imimport xlrd

# 读取文件
work_book = xlrd.open_workbook("my.xlsx")
# 获取所有所有表格名称
print(work_book.sheet_names())
# 选取一个表
sheet = work_book.sheet_by_index(0)
# 表格名称
print(sheet.name)
# 行数
print(sheet.nrows)
# 列数
print(sheet.ncols)
# 某行全部
print(sheet.row(6))
print(sheet.row(13))
# 某列全部
print(sheet.col(4))
# 某行列区间
print(sheet.row_slice(6, start_colx=0, end_colx=4))
# 某列行区间
print(sheet.col_slice(3, start_rowx=0, end_rowx=3))
# 某行类型0:空 1:str 2:num 3:date | 值
print(sheet.row_types(1), sheet.row_values(6))

# 单元格
print(sheet.cell(6,0).value) # 取值
print(sheet.cell(6,0).ctype) # 取类型
print(sheet.cell_value(6,0)) # 直接取值
print(sheet.cell_type(6,0)) # 直接取类型

# 0:以1900年为基准 1:以1904年为基准
print(xlrd.xldate_as_datetime(sheet.cell(6, 0).value, 0))
import xlwt
# 创建工作簿
work = xlwt.Workbook()
# 创建一个表
sheet = work.add_sheet("员工信息数据")
# 创建一个字体对象
# font = xlwt.Font()
# font.name = "Times New Roman"  # 字体名称
# font.bold = True  # 加粗
# font.italic = True  # 斜体
# font.underline = True  # 下划线
# 创建一个样式对象
# style = xlwt.XFStyle()
# style.font = font
keys = ['Owen', 'Zero', 'Egon', 'Liuxx', 'Yhh']
# 写入标题
c = 0
for k in keys:
    # sheet.write(0, keys.index(k), k, style)
    # sheet.write(0, keys.index(k), k)

    sheet.write(keys.index(k) + 5, 2, k if k != 'Egon' else 'cool')
    # sheet.write(c, c, k)
    # c += 1

# 写入数据
# sheet.write(1, 0, 'cool', style)
# sheet.write(0, 0, 'cool')
# 保存至文件
work.save("new_my.xls")
原文地址:https://www.cnblogs.com/yangmeichong/p/10873920.html