模块(time、datetime、os、random、日志logging、hashlib)

【一】time模块

【1】表示时间的三种方式

  • 时间戳
  • 元组(struct_time)
  • 格式化的时间字符串:
    • 格式化的时间字符串(Fromat String):'1999-12-06'

【2】时间转换

(1)导入时间模块

import time

(2)时间戳

[1]生成时间戳
import time

# 生成时间戳 , 时间戳是浮点数类型
time_str = time.time()
print(time_str, type(time_str))
# 1700832484.27306 <class 'float'>
  • 时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数。
  • 时间戳是使用数字签名技术产生的数据,签名的对象包括了原始文件信息、签名参数、签名时间等信息。
  • 时间戳系统用来产生和管理时间戳,对签名对象进行数字签名产生时间戳,以证明原始文件在签名时间之前已经存在。
[2]时间戳转换为时间元组(UTC时间)
  • 将时间戳转换为UTC时间,与英国伦敦当地时间一致
import time

# 生成时间戳
time_str = time.time()

# 时间戳转换为时间元组类型(UTC时间 --- 英国伦敦当地时间)
print(time.gmtime(time_str))
# ime.struct_time(tm_year=2024, tm_mon=4, tm_mday=19, tm_hour=7, tm_min=47, tm_sec=27, tm_wday=4, tm_yday=110, tm_isdst=0)
[3]时间戳转换为时间元组(当地时间)
  • 当地时间。例如我们现在在北京执行这个方法:与UTC时间相差8小时,UTC时间+8小时 = 北京时间
import time

# 生成时间戳
time_str = time.time()

# 时间戳转换为时间元组类型(当地时间)
print(time.localtime(time_str))
# time.struct_time(tm_year=2024, tm_mon=4, tm_mday=19, tm_hour=15, tm_min=47, tm_sec=53, tm_wday=4, tm_yday=110, tm_isdst=0)

(3)时间字符串(strftime)

import time

time_now = time.strftime("%Y-%m-%d %X")
print(time_now)
# 2024-04-19 15:49:57

time_now_new = time.strftime("%Y-%m-%d %H-%M-%S")
print(time_now_new)
# 2024-04-19 15-50-10
  • Python中时间日期格式化符号

(4)时间元组

  • localtime将一个时间戳转换为当前时区的struct_time
import time

date_time = time.localtime()

# 查看元组形式的时间
print(date_time)
# time.struct_time(tm_year=2024, tm_mon=4, tm_mday=19, tm_hour=15, tm_min=54, tm_sec=8, tm_wday=4, tm_yday=110, tm_isdst=0)

# 可以通过直接 `.` 取每个值
print(date_time.tm_year)  # 获取到当前年
# 2024

print(date_time.tm_mon) # 当前月
print(date_time.tm_mday) # 当前日
print(date_time.tm_wday) # 当前周
print(date_time.tm_yday) # 当前天数 在一年中的第 x 天

# 可以通过索引取每个单独的值
print(date_time[0]) # 获取到元组索引为0的元素
# 2024
  • struct_time 元组共有9个元素共九个元素:
    • (年,月,日,时,分,秒,一年中第几周,一年中第几天等)

(5)结构化时间

[1]time.strftime(结构化时间--->时间字符串)
  • 结构化时间参数若不传,则显示当前时间
import time

# 格式化当前时间
time_str = time.strftime("%Y-%m-%d %X")
print(time_str)  # 2024-04-19 15:59:29

print(time.localtime(time.time()))
# time.struct_time(tm_year=2024, tm_mon=4, tm_mday=19, tm_hour=15, tm_min=59, tm_sec=43, tm_wday=4, tm_yday=110, tm_isdst=0)

# 格式化当前时间元组
time_local = time.strftime("%Y-%m-%d", time.localtime(time.time()))
print(time_local)  # 2024-04-19
[2]time.mktime(时间戳--->元组时间戳)
import time

print(time.time())  # 1713513856.3952584
time_tuple = time.localtime(time.time())
time_str = time.mktime(time_tuple)
print(time_str)  # 1713513873.0
[3]time.strptime(时间字符串-->时间元组)
import time

time_str = time.strptime("2024-4-19", "%Y-%m-%d")
print(time_str)
# time.struct_time(tm_year=2024, tm_mon=4, tm_mday=19, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=4, tm_yday=110, tm_isdst=-1)

time_local = time.strptime("4/19/2024", "%m/%d/%Y")
print(time_local)
# time.struct_time(tm_year=2024, tm_mon=4, tm_mday=19, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=4, tm_yday=110, tm_isdst=-1)
[4]time.asctime(结构化时间--> %a %b %d %H:%M:%S %Y串)
  • time.asctime(结构化时间)
    • 如果不传参数,直接返回当前时间的格式化串
import time

time_str = time.asctime(time.localtime(time.time()))
print(time_str)
# Fri Apr 19 16:07:39 2024

time_local = time.asctime()
print(time_local)
# Fri Apr 19 16:07:54 2024
  • time.ctime(时间戳)
    • 时间戳 --> %a %b %d %H:%M:%S %Y串
    • 如果不传参数,直接返回当前时间的格式化串
import time

time_str = time.ctime(time.time())
print(time_str)
# Fri Apr 19 16:08:10 2024

time_local = time.ctime()
print(time_local)
# Fri Apr 19 16:08:27 2024

【3】总结

# time.time() 		生成时间戳
# time.sleep() 		随机睡眠多长时间
# time.strftime()	格式化输出当前时间
# time.strptime()   将时间字符串格式转换为时间元组
# time.localtime()  拿到当前时间的时间元组格式

【二】datetime模块

(1)导入模块

import datetime

(2)自定义日期并格式化

res = datetime.date(2020,8,9)
print(res)  # 2020-08-09

(3)获取本地时间

[1]年月日

import datetime

now_date = datetime.date.today()
print(now_date)
# 2024-04-19

[2]年月日时分秒

import datetime

now_time = datetime.datetime.today()
print(now_time)
# 2024-04-19 16:15:09.606637
  • 无论是年月日,还是年月日时分秒对象
  • 都可以调用以下方法获取针对性的数据
    • 以datetime对象举例
# print(datetime.date.today()) # 获取今天年月日
# print(datetime.datetime.today().year) #获取年
# print(datetime.datetime.today().month) #获取月
# print(datetime.datetime.today().day) #获取日
# print(datetime.datetime.today().hour) #获取时
# print(datetime.datetime.today().minute) #获取分
# print(datetime.datetime.today().second) #获取秒
# print(datetime.datetime.today().weekday())  # 0是周一  6是周日

[3]timedelta 时间对象

  • 作用:对时间进行推迟和提前
import datetime
now_day = datetime.date.today()
# # 生成一个时间日期对象
time_change = datetime.timedelta(days=8) 
print(now_day + time_change) #获取当天日期后的8天的日期
print(now_day - time_change) #获取当天日期的8天前的日期

[4]日期对象与timedelta之间的关系

  • 日期对象 = 日期对象 +/- timedelta对象
  • timedelta对象 = 日期对象 +/- 日期对象
import datetime

day_now = datetime.date.today()
day_to = datetime.timedelta(days=6)

print(day_now)
# 2024-04-19
print(day_to)
# 6 days, 0:00:00

# 日期对象 = 日期对象 +/- timedelta对象
now_date = day_now + day_to
print(now_date)  # 2024-04-25
print(type(now_date))  # <class 'datetime.date'>

# timedelta对象 = 日期对象 +/- 日期对象
lta2 = day_now - now_date
print(lta2)  # -6 days, 0:00:00
print(type(lta2))  # <class 'datetime.timedelta'>

birthday = datetime.date(1976, 11, 24)
now_date = datetime.date.today()
today = datetime.date.today()
days = now_date - birthday
print('生日:{}'.format(birthday)) # 生日:1976-11-24
print('今天的日期:{}'.format(today)) # 今天的日期:2024-04-19
print('距离生日还有{}天'.format(days)) # 距离生日还有17313 days, 0:00:00天

【三】os模块

# os 模块 可以操作本地的文件夹及文件
# 创建删除文件件/文件
# 执行本地的cmd命令
# 拼接路径
# __file__ 表示当前文件

【1】文件路径相关的操作

(1)获取当前文件绝对路径(abspath)

import os
# 获取当前文件路径
file_path = os.path.abspath(__file__)
print(file_path)
# E:\PythonProjects\07常用模块学习\03os模块.py

(2)获取当前文件所在文件夹路径(dirname)

import os

# 获取当前文件所在文件夹路径
file_path = os.path.dirname(__file__)
print(file_path)
# E:\PythonProjects\07常用模块学习

(3)判断当前路径是否存在(exists)

import os

# 指定一个文件路径 (r表示转义符,会将 \ 转换为 \\ )
file_path_one = r'E:\PythonProjects\07常用模块学习'
is_true_one = os.path.exists(file_path_one)
print(is_true_one)
# True

# 指定一个不存在文件路径
file_path_two = r'E:\PythonProjects\07常用模块学习\img'
is_true_two = os.path.exists(file_path_two)
print(is_true_two)
# False

(4)拼接文件路径(join)

import os

BASE_DIR = os.path.dirname(__file__)
print(BASE_DIR)
# E:\PythonProjects\07常用模块学习

file_name = 'img'
# join 后面可以跟多个路径
file_path = os.path.join(BASE_DIR, file_name)
print(file_path)
# E:\PythonProjects\07常用模块学习\img
# os.path.join('地址','地址','地址') --->前面放根路径,后面放自己的文件夹名字 --->绝对路径

(5)切割路径(split)

import os

# 获取当前文件所在路径
BASE_DIR = os.path.abspath(__file__)
# 对路径进行切割 --- 切割成元组
file_path_list = os.path.split(BASE_DIR)
print(file_path_list)
# ('E:\\PythonProjects\\07常用模块学习', '03os模块.py')

(6)获取结尾文件/文件夹名(basename)

import os

BASE_DIR = os.path.dirname(__file__)

file_path = os.path.join(BASE_DIR, 'img')
print(file_path)

# 返回path最后的文件名
# 如何 path以/或\结尾,那么就会返回空值。
# 即 os.path.split(path) 的第二个元素
is_true = os.path.basename(file_path)
print(is_true)
# img

file_path_one = os.path.abspath(__file__)
print(file_path_one)
# E:\PythonProjects\07常用模块学习\03os模块.py

print(os.path.basename(file_path_one))
# 03os模块.py

(7)当前路径是否是文件(isfile)

import os

BASE_DIR = os.path.dirname(__file__)
print(BASE_DIR)
# E:\PythonProjects\07常用模块学习

file_path = os.path.abspath(__file__)
print(file_path)
# E:\PythonProjects\07常用模块学习\03os模块.py

is_true_one = os.path.isfile(BASE_DIR)
print(is_true_one)
# False

is_true_two = os.path.isfile(file_path)
print(is_true_two)
# True

8)当前路径是否为绝对路径(isabs)

import os

file_path_one = r'E:\PythonProjects\07常用模块学习'
file_path_two = r'../07常用模块学习'

is_true_one = os.path.isabs(file_path_one)
print(is_true_one)
# True

is_true_two = os.path.isabs(file_path_two)
print(is_true_two)
# False

(9)当前文件目录是否存在

import os

file_path_one = r'E:\PythonProjects\07常用模块学习'
file_path_two = r'../07常用模块学习'

is_true_one = os.path.isdir(file_path_one)
print(is_true_one)
# True

is_true_two = os.path.isdir(file_path_two)
print(is_true_two)
# True

(10)获取当前文件或目录的最后访问时间(getatime)

import os, time

BASE_DIR = os.path.dirname(__file__)
file_path = os.path.abspath(__file__)

file_base_time = os.path.getatime(BASE_DIR)
print(file_base_time)  # 1713522439.8275347
print(time.strftime("%Y-%m-%d  %H-%M-%S", time.localtime(file_base_time)))
# 2024-04-19  18-27-46

file_time = os.path.getatime(file_path)
print(time.strftime("%Y-%m-%d  %H-%M-%S", time.localtime(file_time)))
# 2024-04-19  18-30-27

(11)获取当前文件或者目录的创建时间(getctime)

import os, time

BASE_DIR = os.path.dirname(__file__)
file_path = os.path.abspath(__file__)

file_base_time = os.path.getctime(BASE_DIR)
print(file_base_time)  # 1711427709.0155363
print(time.strftime("%Y-%m-%d  %H-%M-%S", time.localtime(file_base_time)))
# 2024-03-26  12-35-09

file_time = os.path.getctime(file_path)
print(time.strftime("%Y-%m-%d  %H-%M-%S", time.localtime(file_time)))
# 2024-04-19  11-47-01

(12)返回当前文件或路径的最后修改时间(getmtime)

import os, time

BASE_DIR = os.path.dirname(__file__)
file_path = os.path.abspath(__file__)

file_base_time = os.path.getmtime(BASE_DIR)
print(file_base_time)  # 1713523216.0985684
print(time.strftime("%Y-%m-%d  %H-%M-%S", time.localtime(file_base_time)))
# 2024-04-19  18-40-32

file_time = os.path.getmtime(file_path)
print(time.strftime("%Y-%m-%d  %H-%M-%S", time.localtime(file_time)))
# 2024-04-19  18-41-19

(13)返回当前文件的大小(getsize)

import os

BASE_DIR = os.path.dirname(__file__)
file_path = os.path.abspath(__file__)

file_base_time = os.path.getsize(BASE_DIR)
print(file_base_time)  # 4096

file_time = os.path.getsize(file_path)
print(file_time)  # 501

【补充】获取当前系统信息

# win \
# mac / linux  /
# 输出操作系统特定的路径分隔符
print(os.sep) # \
# 输出当前平台使用的行终止符
print(os.linesep) # 
# 输出用于分割文件路径的字符串
print(os.pathsep)# ;
# 输出字符串指示当前使用平台
print(os.name) # nt
# win下为 'nt'
# Linux下为 'posix'

【记住的重点】

# os.path.join() 拼接路径
# os.path.dirname(__file__) 获取当前文件所在的文件夹路径
# os.path.abspath(__file__)获取当前文件所在的文件路径
# os.path.exists() 判断路径是否存在
# os.mkdir('路径') 创建单级文件夹
# os.makedirs('路径') 创建多级文件夹
# os.makedirs('路径', exist_ok=True) 自动创建不存在的多级文件夹路径
# os.remove('地址') 删除多级文件夹,文件夹内必须是空的,只要有内容就删不掉
# 

【四】random模块

import random

【1】随即小数

# 【1】默认区间的小数 是区间 0 - 1 之间的小数
print(random.random())
# 【2】指定区间 给定区间内的小数
print(random.uniform(1, 3))

【2】随机整数

(1)随机区间整数(randint)

  • 大于等于1且小于等于5之间的整数
import random

# 大于等于1且小于等于5之间的整数
res = random.randint(1, 5)
print(res)
# 4

(2)随机区间奇偶数(randrange)

  • 大于等于1且小于10之间的奇数
import random

# 随机区间奇数 (开始,结束,步长)
res_one = random.randrange(1, 10, 2)
print(res_one)
# 7

【3】随机返回值

(1)随机返回一个(choice)

  • 随机选择一个返回
import random

choice = ["Chosen", 'chosen', 'Meng', 18]

res = random.choice(choice)

print(res)
# Meng

(2)随机指定个数(sample)

  • 指定待选择项和返回的个数
import random

choice = ["Chosen", 'chosen', 'Meng', 18]

# 待选择项,返回的个数
res = random.sample(choice,k=3)

print(res)
# ['Meng', 'Chosen', 18]

【4】打乱顺序(shuffle)

import random

item = [1, 3, 5, 7, 9]
random.shuffle(item)
print(item)
# [5, 1, 3, 7, 9]

random.shuffle(item)
print(item)
# [5, 9, 7, 1, 3]

【5】随机生成验证码

# 随机大小写字母 + 随机数字
# 补充知识点 chr(数字) ---> 返回指定ASCII码对应的字符
# 做一个能生成 6 位的验证码
import random
def get_verify_code(n):
    code = ''
    for i in range(n):
        random_int = str(random.randint(0, 9))  # 0-9之间的整数
        random_upper = chr(random.randint(65, 90))  # A-Z之间的字母
        random_lower = chr(random.randint(97, 122))  # a-z之间的字母
        temp = random.choice([random_int, random_upper, random_lower])
        code += temp
    return code


res = get_verify_code(6)
print(res)

【五】logging模块

# logging 模块 记录 log 记录日志的模块

import logging
import logging.config
import os
import sys

try:
    # 想要给日志上色就安装这个模块
    # pip install coloredlogs :::>>> 给日志上个色
    import coloredlogs
except Exception as e:
    if str(e) == "No module named 'coloredlogs'":
        pass

# 自定义日志级别
CONSOLE_LOG_LEVEL = "INFO"
FILE_LOG_LEVEL = "DEBUG"

# 自定义日志格式
# 打印在文件里的格式:时间戳 + 线程名 + 线程ID + 任务ID + 发出日志调用的源文件名 + 发出日志调用的源代码行号 + 日志级别 + 日志消息正文
# [2023-06-04 15:16:05][MainThread:22896][task_id:root][调用.py:12][INFO][这是注册功能]
STANDARD_FORMAT = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d][%(levelname)s][%(message)s]'
# 打印在控制台的格式:日志级别 + 时间戳 + 发出日志调用的源文件名 + 发出日志调用的源代码行号 + 日志消息正文
# [INFO][2023-06-04 15:37:28,019][调用.py:12]这是注册功能

SIMPLE_FORMAT = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
'''
参数详解:
-1.%(asctime)s: 时间戳,表示记录时间
-2.%(threadName)s: 线程名称
-3.%(thread)d: 线程ID
-4.task_id:%(name)s: 任务ID,即日志记录器的名称
-5.%(filename)s: 发出日志调用的源文件名
-6.%(lineno)d: 发出日志调用的源代码行号
-7.%(levelname)s: 日志级别,如DEBUG、INFO、WARNING、ERROR、CRITICAL等
-8.%(message)s: 日志消息正文
'''

# 日志文件路径
BASE_DIR = os.path.dirname(__file__)
# os.getcwd() : 获取当前工作目录,即当前python脚本工作的目录路径
# 拼接日志文件路径 : 当前工作路径 + “logs”(日志文件路径)
LOG_PATH = os.path.join(BASE_DIR, "logs")
# OS模块 : 创建多层文件夹
# exist_ok=True 的意思是如果该目录已经存在,则不会抛出异常。
os.makedirs(LOG_PATH, exist_ok=True)
# log日志文件路径 : 路径文件夹路径 + 日志文件
LOG_FILE_PATH = os.path.join(LOG_PATH, 'Logs.log')

# 日志配置字典
LOGGING_DIC = {
    # 日志版本
    'version': 1,
    # 表示是否要禁用已经存在的日志记录器(loggers)。
    # 如果设为 False,则已经存在的日志记录器将不会被禁用,而是可以继续使用。
    # 如果设为 True,则已经存在的日志记录器将会被禁用,不能再被使用。
    'disable_existing_loggers': False,
    # 格式化程序:用于将日志记录转换为字符串以便于处理和存储。
    # 格式化程序定义了每个日志记录的输出格式,并可以包括日期、时间、日志级别和其他自定义信息。
    'formatters': {
        # 自定义格式一:年-月-日 时:分:秒
        'standard': {
            # 自定义日志格式 :时间戳 + 线程名 + 线程ID + 任务ID + 发出日志调用的源文件名 + 发出日志调用的源代码行号 + 日志级别 + 日志消息正文
            # 这里要对应全局的 STANDARD_FORMAT 配置
            'format': STANDARD_FORMAT,
            # 时间戳格式:年-月-日 时:分:秒
            'datefmt': '%Y-%m-%d %H:%M:%S'  # 时间戳格式
        },
        # 自定义格式二:
        'simple': {
            # 自定义日志格式:# 日志级别 + 时间戳 + 发出日志调用的源文件名 + 发出日志调用的源代码行号 + 日志消息正文
            # 这里要对应全局的 SIMPLE_FORMAT 配置
            'format': SIMPLE_FORMAT
        },
    },
    # 过滤器
    'filters': {},
    # 日志处理器
    'handlers': {
        # 自定义处理器名称 - 输出到控制台屏幕
        'console': {
            # 设置日志等级 为INFO
            'level': CONSOLE_LOG_LEVEL,
            # 表示该处理器将输出日志到流(stream):日志打印到控制台
            'class': 'logging.StreamHandler',
            # 日志打印格式:日志级别 + 时间戳 + 发出日志调用的源文件名 + 发出日志调用的源代码行号 + 日志消息正文
            # 这里的配置要对应 formatters 中的 simple 配置
            'formatter': 'simple'
        },
        # 自定义处理器名称 - 输出到文件
        'default': {
            # 自定义日志等级
            'level': FILE_LOG_LEVEL,
            # 标准输出到文件
            'class': 'logging.handlers.RotatingFileHandler',
            # 日志打印格式:年-月-日 时:分:秒
            # 这里的配置要对应 formatters 中的 standard 配置
            'formatter': 'standard',
            # 这里 要注意声明配置文件输出端的文件路径
            'filename': LOG_FILE_PATH,
            # 限制文件大小:1024 * 1024 * 5 = 5242880,意味着这个变量的值是 5MB(兆字节)
            'maxBytes': 1024 * 1024 * 5,
            # 表示保留最近的5个日志文件备份。
            # 当日志文件达到最大大小限制时,将会自动轮转并且保留最新的5个备份文件,以便查看先前的日志记录。
            # 当日志文件达到最大大小限制时,会自动进行轮转,后续的文件名将会以数字进行命名,
            # 例如,第一个备份文件将被重命名为原始日志文件名加上".1"的后缀,
            # 第二个备份文件将被重命名为原始日志文件名加上“.2”的后缀,
            # 以此类推,直到保留的备份数量达到设定的最大值。
            'backupCount': 5,
            # 日志存储文件格式
            'encoding': 'utf-8',
        },
    },
    # 日志记录器,用于记录应用程序的运行状态和错误信息。
    'loggers': {
        # 空字符串作为键 能够兼容所有的日志(当没有找到对应的日志记录器时默认使用此配置)
        # 默认日志配置
        '': {
            # 日志处理器 类型:打印到控制台输出 + 写入本地日志文件
            'handlers': ['default', 'console'],
            # 日志等级 : DEBUG
            'level': 'DEBUG',
            # 默认情况下,当一个日志消息被发送到一个Logger对象且没有被处理时,该消息会被传递给它的父Logger对象,以便在更高层次上进行处理。
            # 这个传递过程称为“传播(propagation)”,而propagate参数指定了是否要使日志消息向上传播。
            # 将其设置为True表示应该传播消息到上一级的Logger对象;如果设置为False则不传播。
            # 表示异常将会在程序中继续传播
            # 也就是说,如果一个异常在当前的代码块中没有被处理,它将会在上级代码块或调用函数中继续向上传递,直到被某个代码块捕获或者程序退出。
            # 这是 Python 中异常处理机制的默认行为。
            # 如果将 'propagate' 设置为 False,则异常不会被传播,即使在上级代码块中没有处理异常的语句,程序也会忽略异常并继续执行。
            'propagate': True,
        },
    },
}


def set_logging_color(name='', ):
    # 初始化日志处理器 - 使用配置字典初始化日志处理器(将自定义配置加载到日志处理器中)
    # logging.basicConfig(level=logging.WARNING)
    logging.config.dictConfig(LOGGING_DIC)
    # 实例化日志处理器对象 - 并赋予日志处理器等级
    logger = logging.getLogger(name)
    # 将logger对象传递给coloredlogs.install()函数,并执行该函数以安装彩色日志记录器,使日志信息在控制台上呈现为带有颜色的格式。
    # 具体来说,该函数会使用ANSI转义序列在终端上输出日志级别、时间戳和消息等信息,并按日志级别使用不同的颜色来区分它们。这可以让日志信息更易于阅读和理解。
    coloredlogs.install(logger=logger)
    # 禁止日志消息向更高级别的父记录器(如果存在)传递。
    # 通常情况下,当一个记录器发送一条日志消息时,该消息会被传递给其所有祖先记录器,直到传递到根记录器为止。但是,通过将logger.propagate设置为False,就可以阻止该记录器的消息向上层传递。
    # 换句话说,该记录器的消息只会被发送到该记录器的处理程序(或子记录器)中,而不会传递给祖先记录器,即使祖先记录器的日志级别比该记录器要低。
    # 这种方法通常适用于需要对特定记录器进行控制,并希望完全独立于其祖先记录器的情况。
    # 确保 coloredlogs 不会将我们的日志事件传递给根 logger,这可以防止我们重复记录每个事件
    logger.propagate = False
    # 配置 日志颜色
    # 这段代码定义了一个名为coloredFormatter的变量,并将其赋值为coloredlogs.ColoredFormatter。
    # 这是一个Python库中的类,用于创建带有彩色日志级别和消息的格式化器。
    # 该变量可以用作日志记录器或处理程序的格式化程序,以使日志输出更易于阅读和理解。
    coloredFormatter = coloredlogs.ColoredFormatter(
        # fmt表示格式字符串,它包含了一些占位符,用于在记录日志时动态地填充相关信息。
        # [%(name)s]表示打印日志时将记录器的名称放在方括号内,其中name是一个变量名,将被记录器名称所替换。
        # %(asctime)s表示打印日志时将时间戳(格式化为字符串)插入到消息中,其中asctime是时间的字符串表示形式。
        # %(funcName)s表示打印日志时将函数名插入到消息中,其中funcName是函数的名称。
        # %(lineno)-3d表示打印日志时将行号插入到消息中,并且将其格式化为3位数字,其中lineno表示行号。
        # %(message)s表示打印日志时将消息本身插入到消息中。
        # 综合起来,这个格式字符串将在记录日志时输出以下信息:记录器名称、时间戳、函数名称、行号和日志消息。
        # 记录器名称 + 时间戳 + 函数名称 + 行号 + 日志消息
        # [root] 2023-06-04 16:00:57 register 15   this is an info message
        fmt='[%(name)s] %(asctime)s %(funcName)s %(lineno)-3d  %(message)s',
        # 级别颜色字典
        level_styles=dict(
            # debug 颜色:白色
            debug=dict(color='white'),
            # info 颜色:蓝色
            info=dict(color='blue'),
            # warning 颜色:黄色 且 高亮
            warning=dict(color='yellow', bright=True),
            # error 颜色:红色 且 高亮 且 加粗
            error=dict(color='red', bold=True, bright=True),
            # critical 颜色:灰色 且 高亮 且 背景色为红色
            critical=dict(color='black', bold=True, background='red'),
        ),
        # 这段代码定义了一个名为field_styles的字典 , 其中包含四个键值对。
        # 每个键代表日志记录中的不同字段,而每个值是一个字典,它指定了与该字段相关联的样式选项。
        # 具体来说,这些字段和样式选项如下:
        # name:指定记录器的名称,将使用白色颜色。
        # asctime:指定日志记录的时间戳,将使用白色颜色。
        # funcName:指定记录消息的函数名称,将使用白色颜色。
        # lineno:指定记录消息的源代码行号,将使用白色颜色。
        field_styles=dict(
            name=dict(color='white'),
            asctime=dict(color='white'),
            funcName=dict(color='white'),
            lineno=dict(color='white'),
        )
    )

    ## 配置 StreamHandler:终端打印界面
    # 这行代码定义了一个名为ch的日志处理器。
    # 具体来说,它是logging.StreamHandler类的一个实例,用于将日志输出到标准输出流(即控制台)中。
    # 在创建StreamHandler对象时,需要指定要使用的输出流
    # 因此stream=sys.stdout参数指定了该对象将把日志写入到标准输出流中.
    ch = logging.StreamHandler(stream=sys.stdout)
    # 这段代码是 Python 中用于设置日志输出格式的语句。
    # 它使用了一个名为 coloredFormatter 的格式化器对象,并将其传递给 ch.setFormatter() 方法来指定输出日志的样式。
    # 具体来说,ch 是一个 Logger 对象,它代表了整个日志系统中的一个记录器。
    # 可以通过该对象来控制日志的级别、输出位置等行为。而 setFormatter() 方法则用于设置该 Logger 输出日志的格式化方式
    # 这里传递的是 coloredFormatter 格式化器对象。
    # 在实际应用中,coloredFormatter 可以是自定义的 Formatter 类或者已经存在的 Formatter 对象。
    # 这里 将 coloredFormatter - 彩色输出日志信息的格式化器 传入进去。
    ch.setFormatter(fmt=coloredFormatter)
    # 这段代码是在Python中添加一个日志记录器(logger)的处理器(handler)。其中,hdlr参数是指定要添加到记录器(logger)中的处理器(handler)对象,ch是一个代表控制台输出的处理器对象。
    # 这行代码的作用是将控制台输出的信息添加到日志记录器中。
    logger.addHandler(hdlr=ch)
    # 这段代码用于设置日志级别为DEBUG,也就是最低级别的日志记录。
    # 意思是在程序运行时,只有DEBUG级别及以上的日志信息才会被记录并输出,而比DEBUG级别更低的日志信息则不会被记录或输出。
    # DEBUG(调试)、INFO(信息)、WARNING(警告)、ERROR(错误)和CRITICAL(严重错误)。
    logger.setLevel(level=logging.DEBUG)
    # 返回日志生成对象
    return logger


def get_logger(name='', ):
    '''
    :param name: 日志等级
    :return:
    '''
    # 初始化日志处理器 - 使用配置字典初始化日志处理器(将自定义配置加载到日志处理器中)
    logging.config.dictConfig(LOGGING_DIC)
    # 实例化日志处理器对象 - 并赋予日志处理器等级
    logger = logging.getLogger(name)
    # 返回日志生成对象
    return logger


if __name__ == "__main__":
    # # 示例:
    # # (1)初始化日志处理器 - 使用配置字典初始化日志处理器(将自定义配置加载到日志处理器中)
    # logging.config.dictConfig(LOGGING_DIC)
    # # (2)实例化日志处理器对象 - 并赋予日志处理器等级
    # # # logger1 = logging.getLogger('') # 默认为 '' 即以默认配置进行实例化
    # logger1 = logging.getLogger('')
    # # (3)当日志发生时,打印的提示语
    # logger1.debug('这是日志生成语句')
    logger_nor = get_logger(name='user')
    logger_nor.info(msg="this is a info message")
    logger_col = set_logging_color()
    logger_col.info(msg="this is a debug message")

【六】hashlib模块

# 摘要算法 : 只能加密不能解密
# 加密算法 : 用方法加密 加密后的字符串可以解密

【1】摘要算法

  • 摘要算法又称哈希算法、散列算法。
  • 它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。
  • 摘要算法就是通过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest
    • 目的是为了发现原始数据是否被人篡改过
  • 摘要算法之所以能指出数据是否被篡改过,就是因为摘要函数是一个单向函数
    • 计算f(data)很容易,但通过digest反推data却非常困难。
    • 而且,对原始数据做一个bit的修改,都会导致计算出的摘要完全不同。

【2】基础

import hashlib
import json


def one():
    # 给原始数据
    data = 'my name is dream'
    # 转成二进制数据
    data = data.encode('utf-8')
    # 创建一个md5对象
    md5 = hashlib.md5()
    # 把原始数据给 md5 对象加密
    md5.update(data)
    # 返回加密后的结果
    # 一种是加密后的 16 进制的 32 长度的字符串
    print(md5.hexdigest())  # c4ca4238a0b923820dcc509a6f75849b
    # 一种是二进制数据
    print(md5.digest())

    # 把常见的这种字符 用md5加密 ---> 扔到数据库里面
    # 数据库中查 又返回
    return md5.hexdigest()

【3】加盐

  • 如果以明文保存用户口令,如果数据库泄露,所有用户的口令就落入黑客的手里。
  • 此外,网站运维人员是可以访问数据库的,也就是能获取到所有用户的口令。
  • 正确的保存口令的方式是不存储用户的明文口令,而是存储用户口令的摘要
  • 由于常用口令的MD5值很容易被计算出来
    • 所以,要确保存储的用户口令不是那些已经被计算出来的常用口令的MD5
    • 这一方法通过对原始口令加一个复杂字符串来实现,俗称“加盐”
import random


def get_verify_code(n):
    code = ''
    for i in range(n):
        random_int = str(random.randint(0, 9))  # 0-9之间的整数
        random_upper = chr(random.randint(65, 90))  # A-Z之间的字母
        random_lower = chr(random.randint(97, 122))  # a-z之间的字母
        temp = random.choice([random_int, random_upper, random_lower])
        code += temp
    return code


def two():
    code = get_verify_code(6)
    # 给原始数据
    data = 'my name is dream'
    # data = data + code
    # data = code + data
    # 转成二进制数据
    data = data.encode('utf-8')
    code = code.encode('utf-8')
    # 创建一个md5对象
    md5 = hashlib.md5()
    # 把原始数据给 md5 对象加密
    md5.update(code)
    md5.update(data)
    # 返回加密后的结果
    # 一种是加密后的 16 进制的 32 长度的字符串
    print(md5.hexdigest())  # c4ca4238a0b923820dcc509a6f75849b
    # 一种是二进制数据
    print(md5.digest())

    # 把常见的这种字符 用md5加密 ---> 扔到数据库里面
    # 数据库中查 又返回

    return md5.hexdigest()


'''
one = one()
two = two()
print(one)  # a9824cf2cad8240003dd8071a4b315d3
print(two)
# 78e3b0d87878746c81a6aee148139459
# c0cd81c3993a68867f1ebeeb7e0b2535
# df200c13893926f399290212fbf8dc26
# b44c1a5212b71ee2bde885dfd8e0484e
'''

【4】案例

def save_data(data):
    with open('user_data.json', 'w') as fp:
        json.dump(obj=data, fp=fp)


def read_data():
    with open('user_data.json', 'r') as fp:
        data = json.load(fp=fp)

    return data


def get_username_password():
    username = input("username :>>>> ").strip()
    password = input("password :>>>> ").strip()
    return username, password


def encrypt_password(password, salt):
    data = password + salt
    data = data.encode()
    md5 = hashlib.md5()
    md5.update(data)
    return md5.hexdigest()


def register():
    username, password = get_username_password()
    print(password)
    salt = get_verify_code(6)
    password = encrypt_password(password, salt)
    save_data({'username': username, "password": password,'salt':salt})


def login():
    username, password = get_username_password()
    user_data_dict = read_data()
    print(password)
    salt = user_data_dict['salt']
    password = encrypt_password(password, salt)
    if username == user_data_dict['username'] and password == user_data_dict['password']:
        print(f"登录成功!")
    else:
        print("登录失败")
register()
login()
posted @ 2024-04-23 18:50  光头大炮  阅读(17)  评论(0编辑  收藏  举报