os模块
# 作用与文件路径创建文件拼接路径等等
import os
1. os.path.abspath(__file__) # 找到当前执行当前代码文件的文件路径
2.os.path.dirname( os.path.abspath(__file__)) # 找到当前执行文件的父级路径
3.os.path.join(path2,'abc.py') # 进行文件路径的拼接
4.os.path.exists("文件路径") # 判断文件是否存在 返回 True False
5.os.makedirs('新创建的文件') # 创建文件夹 最外部文件的子文件夹
6.os.path.isdir("路径") # 判断路径是否是文件夹(文件/文件夹) 返回 True False
7.os.listdir('文件路径') # 查看当前路径下的子文件夹 返回列表
8. os.walk("需要遍历路径") # 查看全部文件夹内的全部文件 返回生成器
9.os.getcwd() # 获取当前脚本执行的路径
10. os.makedirs('dirname/dirname') # 创建文件可以深层创建(文件名称存在报错)
11. print(os.curdir) # 返回当前目录 返回 .
12. print(os.pardir) # 返回父级目录 返回 ..
13. os.removedirs('dirname/dirname') # 可以递归删除
14. print(os.chdir('dirname')) # 改变当前的脚本路径 相当shell中的cd命令
15.os.mkdir('dirname') # 在当前文件下创建单文件夹 相当于shell中的mkdir dirname
16. os.rmdir('dirname') # 删除文件夹 文件不为空删除报错 相当于 shell rkdir dirname
17.其他
print(os.stat('path/filename')) # 获取文件的目录信息
print(os.sep) # 输出特定操作系统下的路径分隔符 win \ linux /
print(os.linesep) # 输入系统下的终止符win '\t\n' linux'\n'
print(os.pathsep) # 系统下的环境变量的分割符 win ; linux :
print(os.name) # 输出字符串 指定当前的系统 win nt linux posix
os.system('dir C:\\Users\\56515\\Desktop\\基本内容复习\\其他') 作用就是在python中执行cmd的命令
print(os.environ) # 查看系统的环境变量(整个电脑的环境变量 全局) key(字符串)=val(字符串) 字典形式规定
18.重点 将路径的\ 转为适合当下系统的斜杠
os.path.normcase('c:/win\\123') # # c:\win\123 win下将所有的斜杠换为左斜杠
19.查看文件下的全部
print(os.path.getsize('../model测试'))
20.判断是不是文件
print(os.path.isfile('bj.txt')) # 判断文件是不是文件 存在返回True 不存在返回False
21.判断是不是绝对路径
print(os.path.isabs(r'C:\b\b\a.txt')) # 如果获取的是绝对路径 返回True 不是返回False
22.print(os.path.getatime('aaa')) # 获取文件和目录最后存取的时间 时间戳
23.print(os.path.getmtime('aaa')) # 获取文件和目录最后修改的时间 时间戳
Path-文件路径
from pathlib import Path
root = Path(__file__) # 获取当前执行文件的绝对路径
ret = root.parent # 获取当前文件的上一级目录 可以无限向后.parent 获取上一级
print(ret) # C:\Users\wkx\Desktop\测试\model测试
res = Path(__file__) / 'a\e.txt' # 进行拼接
print(res) # C:\Users\wkx\Desktop\测试\model测试\测试2.py\a\e.txt
print(res.resolve()) # 左斜杠换为右斜杠 与normcase效果一样
shutil模块
# 用于文件压缩和解压
import shutil
1.删除文件夹或者文件
shutil.rmtree("文件夹或者路径")
2.拷贝文件夹
shutil.copytree("拷贝文件夹路径","拷贝完成后存放的路径")
3.拷贝文件 可以重置文件名称
shutil.copy("拷贝文件路径/没有设置文件名,就用原来的","拷贝完成后存放的路径/文件名")
4.文件夹文件重命名(文件夹移动)
shutil.move("当前文件/文件夹目录","新文件夹/新文件目录/新名字")
5.文件压缩
shutil.make_archive(base_name=r"压缩之后存放的路径",format=r"压缩格式",root_dir=r"需要压缩的文件路径")
6.文件解压
shutil.unpack_archive(filename=r"需要解压的文件路径",extract_dir=r"解压后释放的路径",format=r"解压格式")
sys模块
impot sys
sys.argv # 可以读取解释器后执行的参数
sys.path # 获取模块的搜索路径 初始化时使用pythonpath环境变量的值(在包搜索是先在内存中搜索,在去磁盘中搜索) 打印的是磁盘的包路 可以进行append添加路径
sys.modules # 获取缓存到内存中的包路径(当文件导入时内存没有,从磁盘导入,导入后存放到内存中)
sys.version # 打印当前解释器的版本信息
print(sys.maxsize) # 获取int 最大的值
print(sys.platform) # 获取操作系统平台的名称
sys.exit(0) # 退出程序 当使用这个方法 后面程序不会执行直接退出
random模块
数字模块
import random
print(random.random()) # 获取0-1的随机数小数
random.randint(10,100) # 随机范围获取一个整数 头尾兼顾
print(random.randrange(1, 10)) # 获取1-10之间的整数 顾头不够尾 尾部取不到
random.uniform(10,100) # 获取范围内的值(小数)
lis = [11,22,33,44,55,66,77,88,99]
random.sample(lis,2) # 参数1:列表,元组,集合 参数2:获取随机的数量 根据传入的列表或者其他 根据后面的参数获取某几个值
lis = [11,22,33,44,55,66,77,88,99]
random.choice(lis) # 随机一个元素 列表 元祖 集合
lis = [11,22,33,44,55,66,77,88,99]
print(random.shuffle(lis)) # 打乱原有顺序
hashlib加密模块
import hashlib
md5 = hashlib.md5()
md5.update('66666'.encode('utf-8'))
print(md5.hexdigest()) # ae8b5aa26a3ae31612eec1d1f6ffbce9
密码加盐
# 防止黑客猜出来密码 和 加的其他的干扰信息
# 并且就算知道密码也没有用
import hashlib
md = hashlib.md5()
# 密码
pwd = '123456'
md.update('天王盖地虎'.encode('utf-8')) # 加盐
md.update(pwd.encode('utf-8')) # 实际的密码
md.update('小鸡吃老虎'.encode('utf-8')) # 加盐
print(md.hexdigest()) # 获取的hash内容...
检验文件的完整性
# 推荐 使用将一行一行的进行hash转换
md.update('整个文件')
# 推荐,这样防止内存溢出的问题 文件内容过大:循环的次数特别多速断慢
md.update('文件内的一行一行的数据')
md.update('文件内的一行一行的数据')
md.update('文件内的一行一行的数据')
# 可以这样解决:
with open('文件', mode='rd', encoding='utf-8') as f:
f.seek('随机指定的位置')
f.read() # 读取
md.update('将内容写入到hash中')
# 这样同时的防止了篡改的问题,又校验了文件的内容,速度比读取全部文件要快
moviepy模块
pip install moviepy
from moviepy.editor import VideoFileClip
vid = VideoFileClip("视频路径地址")
print(vid.duration) # 打印视频的时间 按照秒
json模块
跨语言传递数据
import json
dic = {'name':'wkx','age':18}
print(json.dumps(dic)) # 变为json格式的数据
print(json.loads(json.dumps(dic))) # 将json数据转为当前语言使用的数据
time模块
import time
1.获取时间戳 从1970-现在的时间
time.time()
2.获取当前时区
time.timezome
3.睡眠
time.sleep("停止时间按秒计算")
4.获取格林尼标准时间
# Thu Dec 8 20:40:26 2022
print(time.asctime())
5.获取时间格式化
time.localtime() # 可以通过.tm_year 获取内容
# time.struct_time(tm_year=2022, tm_mon=12, tm_mday=8, tm_hour=20, tm_min=40, tm_sec=26, tm_wday=3, tm_yday=342, tm_isdst=0)
6.获取格式化的时间
print(time.strftime('%Y-%m-%d %H:%M:%S %p')) # 2022-12-08 20:40:26 PM
print(time.strftime('%Y-%m-%d %X')) # 2022-12-08 20:40:26
datetime模块
from datetime import datetime
# 对时间的处理 +-
1.获取当前时间
datetime.now() # 获取的datetime 类型
2.获取指定的时间
from datetime import timezone,timedelta
print(timezone(timedelta(hours=8)))
3.两个时间相加
from datetime import datetime ,timedelta
变量1 = datetime.now() 获取当前时间
变量2 = 变量1+timedelta(days=天数 ,minutes=分钟) 相加后的时
4.字符串转换为datetime时间 strftime
变量 = datetime.strptime(需要转换的变量或者字符串,转换格式("%Y-%m-%d"))
获取的变量就是datetime的时间
5.datetime时间转换字符串 strftime
变量1 = datetime.now() # 获取当前的datetime时间
变量2 = 变量1.strftime("转换时间的格式")
%Y年,%m月份,%d天,%H时 %M分 %S秒
datetime.now().strftime('%Y-%m-%d')
7.datetime格式转换时间戳
时间戳 = time.time()
datetime.datetime.fromtimestamp(时间戳) # 直接将时间戳转为时间 1970-01-01 08:55:33
8.获取世界时间
print(datetime.datetime.utcnow()) # 获取世界时间 2022-06-07 12:43:22.664919
格林尼标准时间
from datetime import datetime
1.变为gmt时间格式
GMT_FORMAT = '%a, %d %b %Y %H:%M:%S GMT+0800 (CST)'
print(datetime.utcnow().strftime(GMT_FORMAT)) # Thu, 09 Jun 2022 14:45:30 GMT+0800 (CST)
2.字符串转换
# 变为字符串格式
a = 'Thu, 12 May 2022 23:08:47 GMT+0800 (CST)'
b = datetime.strptime(a, GMT_FORMAT)
print(b)
3.出现格式中缺少GMT+时 进行字符串的替换,在进行转换
a = 'Thu, 12 May 2022 23:08:47 +0800'.replace('+0800', 'GMT+0800 (CST)')
print(datetime.strptime(a, GMT_FORMAT))
时间转换
# 1654436371.0 时间戳
# 1988-03-03 格式化时间
# time.struct_time(tm_year=2022, tm_mon=12, tm_mday=8, tm_hour=20, tm_min=53, tm_sec=10, tm_wday=3, tm_yday=342, tm_isdst=0) 结构化时间
转换 格式化字符串 <-> 结构化时间 <-> 时间戳
1.time时间对象 # 结构化的时间
s_time = time.localtime() # time时间对象 结构化的时间
# time.struct_time(tm_year=2022, tm_mon=12, tm_mday=8, tm_hour=20, tm_min=53, tm_sec=10, tm_wday=3, tm_yday=342, tm_isdst=0)
print(time.mktime(s_time)) # 1654436371.0
2.时间戳转为time时间对象 # 结构化的时间
s_times = time.time() # 1670504092.59126
print(time.localtime(s_times)) # 结构化的时间
# time.struct_time(tm_year=2022, tm_mon=12, tm_mday=8, tm_hour=20, tm_min=54, tm_sec=52, tm_wday=3, tm_yday=342, tm_isdst=0)
3.本地结构化与世界结构化
print(time.localtime()) # 本地时间
print(time.gmtime()) # 世界时间
4.重点:结构化时间对象 转为 格式化时间
s_time = time.localtime() # 结构化
print(time.strftime('%Y-%m-%d', s_time)) # 2022-12-08 格式化
5重点 格式化时间转为结构化时间
print(time.strptime('1988-03-03','%Y-%m-%d'))
# time.struct_time(tm_year=1988, tm_mon=3, tm_mday=3, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=63, tm_isdst=-1)
案例:
目标 累积 +7 天
例如:读出来的时间为字符串
1988-03-03' 转为结构化时间
strp_time = time.strptime('1988-03-03','%Y-%m-%d')
再将转换为时间戳的形式 +7天
mk_time = time.mktime(strp_time) + 7*86400
转为结构化的时间
time.localtime(mk_time)
转为格式化的时间
time.strftime('%Y-%m-%d', time.localtime(mk_time))
re模块
re的方法
import re
正则表达式的语法
re.natch 从头开始匹配 返回一个对象
从头开始找,第一个字符或者参数必须符合匹配规则,不符合返回none
返回第一个匹配到的元素,无论后面有多少个符合条件的,都不返回(只返回第一个符合条件的)
re.search 全局匹配 返回一个对象
匹配到了就返回,返回当前变量中的全部字 符符合的第一个
如果匹配不到就会返回 none
re.findall 把所有符合规则的全部的放到一个元素列表中 返回的是列表
属于全局匹配,不同的是会将全部符合条件的字符存放到一个列表中
如果匹配不到就会返回一个空列表
re.splitall 匹配到的字符串当做列表的分割符
re.sub 匹配的字符并替换
re.fullmatch 全部匹配
注意:
('规则',匹配的参数)
如果返回的是对象同时又想获取匹配到的值 使用group()
# 只有返回的是对象才能使用
例如:
name = '1aaasdll'
print(re.match('[0-9]',name).group()) 获取的就是从头匹配的第一个值1
例如:
name = 'aaa122sdll'
print(re.search('[0-9]', name).group()) 获取到全部匹配的第一个值 1
# 匹配规则字符
. 默认除/n(换行符以外的全部匹配) 匹配任意一个字符 ..就是两个字符
^ 匹配字符的开头 (^ 跟上字符) 例如^a 那么变量 的开头必须是a 相当于强制的从头匹配
$ 匹配尾部字符 (字符$) 例如 b$ 变量的尾部必须是以b结尾 (match不能使用,因为默认从头开始匹配)
* 匹配*号前面的字符 0次或者多次 如果匹配不到就是'' 例如 a* 那么就会匹配到当前变量中 a后面的a字符(有多少个a那么就会匹配多少个)
ab* 那么会匹配到a 也会匹配到b 也会匹配到ab
+ 匹配前一个字符一次或者多次 例如 a+ 从变量中以a开头的取后面的全部的a
name = 'aaaa2sa122aasd8l'
print(re.search('a+', name)) 获取aaaa
? 匹配前一个字符1次或者0次 相当于只匹配第一个符合条件的,直接返回,剩余的符合的不返回
必须字符以 ? 之前的字符为开头
例如:
a? 那么就会匹配 以a开头的返回第一个字符
name = 'aaaa2sa122aasd8l'
print(re.search('a?', name)) # 返回a
name = '0aaaa2sa122aasd8l'
print(re.search('a?', name)) # 返回 ''
{m} 匹配前一个字符的m次
{整数,填写几} 那么就会匹配前一个字符几次
例如:匹配a 4次 也是就是 aaaa 连着
name = '0aaaa2sa122aasd8l'
print(re.search('a{4}', name)) # 返回aaaa
{n,m} 匹配前一个字符n-m次,对前面的字符进行范围的匹配
{整数,整数} 按照返回进行匹配
例如 a{1,4}
name = '0aaaa2sa122aasd8l'
print(re.findall('a{1,4}', name)) ['aaaa', 'a', 'aa']
| 或者的意思 匹配a字符或者b字符
name = '0baaa2sa122aasd8l'
print(re.search('a|b', name)) # b 意思就是有a 就匹配a 没有a就匹配b
(...) 分组匹配
print(re.search('([a-z]+)([0-9]+)', 'ajax123').groups()) # ('ajax', '123')
将括号内的匹配到的参数 放到一块,可以通过groups() 获取分组匹配的全部的内容
groups 只能分组匹配使用
[] 范围匹配 都包含
print(re.search('[a-z]', 'ajax123')) # a
匹配 a-z 的全部范围的字母
也可以[1-9] 匹配
# 等同于 ^ 与 $
\A 匹配 \相当于一个^ 从头开始匹配
\Z 匹配尾部的字符 同 $
# 匹配全部的数字
\d = [1-9]
\d+ = [1-9]+
# 匹配不是数字的全部字符
\D 匹配一个
\D+ 匹配多个
# 匹配 大写a-z 与小写的a-z 与数字 0-9
\w 跟re.后面的匹配模式 进行匹配一个或者多个
\w+ 匹配多个全部 大写a-z 与小写的a-z 与数字 0-9
# 匹配特殊字符
\W
\W+ 匹配全部的
# 匹配空白字符 \t \n \r 全部的字符
\s
\s 匹配全部
# 分组匹配,相当于给每一个分组匹配的内容起一个单独的名字
print(re.search('(?P<name>\d{3})', '13027630227').groupdict()) # {'name': '130'} 获取一个字典形式
# re.split
# 根据re条件进行切割
# 参数1 匹配规则 参数2 匹配的字符 参数3 maxsplit 切割多少次 默认全部切割
# 为什么['wkx', '', 'wuux', 'ww'] 存在一个空值,是因为\d 只能匹配一个 添加一个+进行贪婪匹配就可以
print(re.split('\d', 'wkx16wuux1ww')) # ['wkx', '', 'wuux', 'ww']
# re.findall
# 根据条件获取全部符合条件的字符
print(re.findall('\d', 'wkx16wuux1ww'))
# re.sub
# 用于替换字符串
# 参数1re匹配规则 参数2替换的参数 参数3 需要替换的字符 参数4 count替换的数量 需要替换几个 默认全部替换
print(re.sub('\d+', '++', 'sasd112a332', count=1))
# re.fullmatch
# 整个字符串全部匹配才会返回成功,否则none
# 例如匹配邮箱的 \w+匹配全部的a-z 0-9 @匹配@ \w+匹配当前@后面的字符 \\.转义. (con|cn|edu) 分组匹配这里面的全部符合条件的(分组先匹配)
print(re.fullmatch('\w+@\w+\\.(com|cn|edu)', '565151759@qq.com'))
# re.compile
# 相当于先写规则,返回对象,在用对象.方法 传入需要认证的字符
res = re.compile('\w+@\w+\\.(com|cn|edu)') # 匹配规则
# 相当于先将规则转换为审核语句,在进行匹配 可以提高效率,先让re模块转为判断语句,在将变量传入到判断语句中
res.fullmatch('565151759@qq.com')
'''
标志符号
re.I b不区分大写小进行匹配(默认re模块区分大小写)
print(re.match('a','Abc',re.I)) A
re.M 多行模式 将换行符无视,视为一行
re.S 将.匹配 到任意的符号
re.X 给匹配规则进行添加注释
'''
re的参数
re符号含义
帮助我们在一段文本中提取数据,或者判断一些数据返回结果。
1.如何编写正则表达式
1.提取固定的内容
变量 = re.findall("固定的内容字符串",匹配的内容)
打印出来生成一个列表
2.不固定的内容
[a,b,c] 提取的内容是 a 或b 或c
变量 = re.findall("正则表达式[固定内容]",匹配的内容)
"显示在外面的内容是固定的必须是这个内容开通[内容是不固定的,只要匹配上就能打印出来结果]"
3.匹配除了一些字符的剩下的字符
[^不匹配的字符]
4.匹配范围内的字符
[a-z] 匹配a-z的全部字符
[1-9] 匹配1-9的全部数字
r[a-z] 匹配开头时r的a-z的全部字符例如ra rz re....
5.特殊字符代表
1.
.代表换行符以外的任意字符(只能代表一个字符) 匹配全部符合条件的
例如:"r.o" 匹配r开头 中间是任意的 o结束的任意字符
rto rbo .只能代表一个字符
.+代表换行符以外的任意字符,+号可以匹配任意个字符 贪恋匹配 匹配全部符合条件的
例如:"r.+o" 匹配r开头 中间可以任意个字符(几个或者多个等等) o为结束符号
ryo rtyuio r.......无数个字符o 代表只要结尾是o中间有几个字符就会匹配一个字符
.+? .代表代表换行符以外的任意字符 +?代表匹配最近的符合条件的第一个字符 非贪恋匹配 匹配全部符合条件的第一个字符
例如:"r.+o" 匹配r开头 o结束
例如:raoraao,只能匹配到 rao
2.
\w 代表字母,数字,下划线,汉字 空格不匹配 匹配全部符合条件的
"r\wo" r 开头o 结束 \w只能匹配一个 字母,数字,下划线,汉字
"r\w+o" r 开头o 结束 \w+ 匹配多个 字母,数字,下划线,汉字 只要是r o 中间的全部字符都会匹配
\d 代表匹配整数数字 匹配全部符合条件的
"m\d" 匹配m开头的 后面的第一位数字 \d 代表一个数字
m1 m2 m3
"m\d+" 匹配m 开头 后面的无数个数字,只要是m开头 后面的全部数字都会匹配
m1364d654641...... m13213464513231.....
3.
\s 匹配任意的空白和空格字符
"r\d+\sr" 匹配 r 开头 中间是数字和空格(一个空白字符) r结束 的字符
"\s+" 匹配全部的空白字符
4. *代表重复0次或者多次.
代表*号 前面 的字符 可以出现0次或者n次
"mz*2" m开头 2结尾 中间的z可以是 z zz zzzzzzzzzz.....也可以是空白m2
mzzz2 mz2 m2
5.+代表重复1次或者n次
代表+号 前面 的字符 必须出现1次或者n次
"mz+2" m开头 2结尾 中间的z可以是 z zz zzzzzzzzzz.....
mzzz2 mz2
6.?代表出现0次或者1次
在?前面的字符最少出现0次,最多出现1次
"mz?2" m开头2结尾 z可以使0个或者1个
mz2, m2
7.{n}重复n次
{n}在前面的字符必须出现n次
"mz{2}2" {2}前面的z必须出现2次
{5}前面的z必须出现5次
8.{n,} 必须出现大于等n次
"mz{2,}2" z必须出现2,或者更多
mzz2', mzzzz2, mzzz2, mzzzzzzzzzzz2
9.{n,m} 大于等于n位,小于等于m位的内容
\d{10,15} \d是数字{10,15} 数字要出现最少10位数,最多不能超过15位数
例如 : 1....10 1...11 1...12 1..13 1...14 1..15 这些都能匹配
10.括号()分区
只会提取()内的全部值
"1sd(\d{5})" 只会提取括号内的全部内容
例如:"13027630227, 15896695189"
提取 '02763', '89669'
11.双括号提取
"1(\d)(\d{5})" 提取括号内的全部内容,因为有两个括号索引提取两个内容得到的结果是一个元组
例如:"13027630227, 15896695189"
[('3', '02763'), ('5', '89669')]
括号内套括号
(1(\d{10})) 先提取外部括号内的内容,在提取提取内部括号的内容 两个结果形成一个元组
例如:"13027630227, 15896695189"
[('13027630227', '3027630227'), ('15896695189', '5896695189')]
12| 或者的意思
(2\d{2}|2\w+r) 将两个内容提取出来2\d{2}或者2\w+r的内容
看|前面的正则和后面的正则那个符合条件,如果前面的正则符合条件,只会匹配前面的后面的不匹配
例如:130222r7630227r
['222', '227']
13.re.ASCII 参数代表 不会匹配内部的 中文,只会匹配字母数字下划线
14.起始和结束
起始^
结束$
可以用于 用户校验
15.\
转义利用\进行转义
typing模块
官方文档:https://docs.python.org/3/library/typing.html
声明的模块 将变量进行声明处理
声明方式函数:
def greeting(name: str) -> str:
return 'Hello ' + name
def abc(name: str) -> list[int or str]
变量声明方式
name: str = 'xxxxx'
# 其他方式可以看文档
pickle模块
作用:将变量转为字节内容,将字节转为变量
# 建议只能进行数据存档,不建议跨平台交互
import pickle
# 序列化为字节模式
res = pickle.dumps('667')
print(res)
#b'\x80\x04\x95\x07\x00\x00\x00\x00\x00\x00\x00\x8c\x03667\x94.'
# 反序列化变为python可以使用的格式
r = pickle.loads(res)
print(r) # 667
subprocess模块
import subprocess
# 指定系统命令的 cmd命令
# 运行的系统命令
# dir 查看目录的全部文件夹
# shell=True相当于打开cmd
# 命令执行的结果正确丢入管道中 stdout=subprocess.PIPE
# 执行命令错误 丢到错误的管道中 stderr=subprocess.Popen
obj = subprocess.Popen('dir /C;/C', shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
# obj 不是命令的结果,是一个对象
print(obj) # <Popen: returncode: None args: 'dir /C'>
print(obj.stdout.read().decode('gbk')) # 这个是获取命令的正确的结果 读出来的字节类型
print(obj.stderr.read().decode('gbk')) # 获取的命令的错误结果 win gbk编码 mac utf-8
# 还可以深入的了解
logging
import logging
******** 基本使用 *********
# logging日志配置内容 的basicConfig配置
logging.basicConfig(
# 1.设置日志的位置:1.在终端打印 2.打印在文件中
# 在win中打开的文件编码有问题:这是为什么:因为logging根据当前系统进行设置的编码格式,win 编码是gbk
filename='login/access.log.text', # 设置日志存入的文件名称和路径
# 2.设置日志格式
# asctime 获取时间
# name 获取日志名称
# levelname 日志的等级
# module 那个模块报的日志
# message 日志的具体信息
# - 和 : 是自己拼接的
format='%(asctime)s - %(name)s - %(levelname)s -%(module)s : %(message)s',
# 3.如果不是用自带的格式,可以自己设置格式
datefmt='%Y-%m-%d %H:%M:%S %p',
# 4.日志级别
# debug 10
# info 20
# warning 30
# error 40
# critical 50
level=10
)
'''
配置部分配置信息打印的内容
2022-06-08 22:17:17 PM - root - DEBUG -logging日志模块 : 打印调试日志
时间 - 日志名称 - 日志等级 - 具体模块报的日志- 日志的具体信息
2022-06-08 22:17:17 PM - root - INFO -logging日志模块 : 打印消息
2022-06-08 22:17:17 PM - root - WARNING -logging日志模块 : 打印消息
2022-06-08 22:17:17 PM - root - ERROR -logging日志模块 : 打印报错
2022-06-08 22:17:17 PM - root - CRITICAL -logging日志模块 : 打印严重错误
如果日志打印的是等级10,那么他就是从等10 自下而上的进行获取
'''
logging.debug('打印调试日志') # debug 状态下的日志
logging.info('打印消息') # 正常输入
logging.warning('打印消息') # 警告日志 有可能出现的问题
logging.error('打印报错') # 错误日志 不是致命的问题
logging.critical('打印严重错误') # 致命的问题,造成程序崩溃
# 在没有设置日志等级时,默认等级是30
# 打印的内容:WARNING:root:打印消息 # warning 类型 root日志名字 内容
# ERROR:root:打印报错
# CRITICAL:root:打印严重错误
*********日志配置字典*******
'''
日志轮转
日志记录这系统的非常重要的内容,不可以轻易的删除,可以用来查看当前系统的状态
就是当随着时间的变化日志就会一直写在同一个文件(造成几个G)就会导致打不开的情况
定期的进行文件分割
日志名
设置的日志名会被记录到,日志信息中
日志名称要具有代表性
如果设置的日志名 在
logger = getLogger('log') 名字不存在
那么logging 模块就会找 loggers 字典中的 '' 的日志进行写入
并且将log 当为新的日志名称
'''
'''
日志文件的配置
'''
'''
1.定义三种日志输出的格式,日志中可能用到下面的字符串格式
%(name)s 日志的名字
%(levelname)s 文本形式日志的等级
%(pathname)s 调用日志输出函数的模块的完整路径名 可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间 用unix标准的表示时间的浮点数表示
%(relativeCreated)d 输出日志信息时的 自logger创建以来的毫秒数
%(asctime)s 输出字符串当前时间 格式 2003-07-08 22:40:45,89
%(thread)d 线程id 可能没有
%(threadName)s 线程名 可能没有
%(process)d 线程ID 可能没有
%(message)s 用户输出的消息
'''
# 写的日志格式:
test_format = '%(asctime)s - %(name)s - %(levelname)s - %(module)s.py - %(funcName)s - %(lineno)d : %(message)s'
standard_format = '%(asctime)s - %(name)s - %(levelname)s - %(module)s.py - %(funcName)s - %(lineno)d : %(message)s'
simple_format = '%(asctime)s - %(name)s - %(levelname)s - %(module)s.py - %(funcName)s - %(lineno)d : %(message)s'
# 日志字典的配置
LOGGING_DIC = {
# 日志版本控制 可以不用配置
'version': 1,
# 关闭已存在日志 默认设置就可以 可以不用配置
'disable_existing_loggers': False,
# 重点 日志格式的意思 +s 代表有多个日志格式 名字不可固定 系统的
'formatters': {
# standard simple test key 属于自定义的名称用来进行日志保存handlers 中 formatter进行使用的
'standard': {
'format': standard_format # format 是logging规定配置名称(不可以修改) val 是对应的日志输出类型
},
'simple': {
'format': simple_format
},
'test': {
'format': test_format
},
},
'filters': {},
# 重点 控制日志输出的位置 日志的接受者 终端 文件1 文件2 都可以是接受者
'handlers': {
# console default other 相当于对接受日志设置一个名称(自定义) 在产生者loggers中handlers列表进行指定 那个接受者可以进行接受
'console': {
'level': "DEBUG", # 日志的等级 可以是字符串 也可以是整数
'class': "logging.StreamHandler", # 调用logging打印到终端类
'formatter': 'simple', # 打印日志的格式 和formatters key中对应(当将配置字典导入配置logging中后,就会使用自动配置的formatters key 配置日志输出类型)
},
# 按照文件的大小进行轮转
'default': {
'level': "DEBUG", # 日志的等级 可以是字符串 也可以是整数
'class': "logging.handlers.RotatingFileHandler", # 日志的轮转类(由logging模块提供的功能)
# 当文件达到指定字节时,将文件中的内容复制到新文件中,清空老文件 (当文件内容达到5m就会创建新的文件夹)
'maxBytes': 1024 * 1024 * 5, # 默认是字节 当达到5m是就进行轮转(将日志文件切割) 达到指定设置的字节大小就会产生新的文件
'backupCount': 1, # 指定产生日志能产生多少份(备份), 超过备份的数量就会被删除
'formatter': "standard", # 指定的输出种类 使用formatters 中下面的指定自定义的key名的日志类型
'filename': "按照文件大小轮转.txt", # 设置文件路径 放到指定的日志文件下中
'encoding': "utf-8" # 设置字节编码
},
# 按照时间轮转,
'times': {
'level': "DEBUG",
'class': 'logging.handlers.TimedRotatingFileHandler', # 按照时间进行轮转
'when': 'S', # 按照什么形式进行轮转 秒 天 还是月 S,秒。M,分。H,时。D,天。midnight,每天凌晨。W{0-6},每周,记住不是从1-7,而是从0开始的。
'interval': 5, # 等待多少秒后才会进行生成新的文件写入日志
'backupCount': 10, # 备份保留的文件,超出的删除
'encoding': "utf-8", # 设置字节编码
'filename': "login/按照时间轮转.txt", # 设置日志的名字 路径
'formatter': "standard", # 指定的输出种类 使用formatters 中下面的指定自定义的key名的日志类型
},
'other': {
'level': "DEBUG", # 日志的等级 可以是字符串 也可以是整数
'class': "logging.FileHandler", # 调用logging保存到文件中类
'formatter': "test", # 指定的输出种类 使用formatters 中下面的指定自定义的key名的日志类型
'filename': "a2.log.txt", # 设置文件路径 放到指定的日志文件下中
'encoding': "utf-8" # 设置字节编码
},
},
# 重点 负责日志不同级别的日志的产生者 产生的日志传给handlers进行输出
'loggers': {
# kkk 产生的日志名字(kkk 就是产生日志的名字可以进行改变)
'kkk': {
# 指定日志给给那个接受者进行保存或者终端显示(将日志传给 指定的接受者) 当kkk产生的日志就会给到列表中的日志接受者进行文件写入
'handlers': ['other', 'console', "default"],
# 日志级别 loggers(第一层的日志级别限制) handlers(第二层日志级别限制) 第一层日志等级负责过滤掉不属于这个等级的日志,第二层日志等级根据第一次获取对应等级日志写入文件
'level': "DEBUG",
'propagate': False, # 默认是True 向上(更高级别level的logger)传递 设置为False
},
'': {
# 这种日志被成为默认日志,如果在大多的都是使用这个日志,在getLogger('名字')
# 没有找到loggers字典中的日志名,那么就会将 getLogger('名字') 名字作为日志名,这样默认日志名字就为动态的存在
'handlers': ["times"],
'level': "DEBUG",
'propagate': False,
}
}
}
# 怎么拿到 配置文件字典中的日志产生这(loggers) kkk
# 1.需要将日志字典加载到logging模块中
from logging import config, getLogger # logging 加载配置文件
# 2.加载配置
config.dictConfig(LOGGING_DIC) # 将配置文件导入 dict 字典类型的 配置文件
# 3.使用getLogger 将配置文件中的日志产生kkk取出来(kkk定义就是日志的名称)
logger = getLogger('日志名')
# 4.产生日志
logger.debug('这是debug日志')
# 使用的路径一定要使用os进行拼接使用,不能手写,会出现路径错误
PyJWT
作用
作为token进行使用,负责对生成的随机值,进行控制(时间控制,加密控制)
pyjwt文档地址:
https://pyjwt.readthedocs.io/en/latest/
JWT
json web toen
分为
jws 签名 速度快 能反推(敏感的数据不能放到这里) 在使用teon方案中 使用jws 签名
jwe 加密 时间太长 不能反推
1.安装 这个是独立的python 的jwt
pip install PyJWT
# 基本使用
import jwt
# 加密
encode = jwt.encode({"内容": "123"}, "324341", algorithm="HS256") # 加密
'''
参数1: 传入加密的内容,用户的id 名字 或者有效期 字典 载荷
参数2: 设置密钥,密钥随便设置 字符串
参数3:传入算法 HS256 ,也可以不传入,默认就是hs256
获得加密后的密串
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJcdTUxODVcdTViYjkiOiIxMjMifQ.D1py3j6-7JDnmqvyGdPwCauRSUzRkDjZKffc9W61Wi0
'''
# 解密
decode = jwt.decode(encode, "324341", algorithms=["HS256"]) # 解密
'''
参数1.将生成的被加密的内变量 获取被加密之前的载荷字典
参数2:传入密钥
参数3:设置加密算法
获得解密后的字典
{"内容": "123"}
'''
代码封装
import jwt
def general_jwt(payload, expiry, secret=None):
'''
生成jwt
:param payload:载荷,也就是存储的用户信息的字典
:param expiry: 过期的时间
:param secret:设置的密钥
:return: 返回一个jwt
'''
# exp 就是jwt内部设置的过期时间的参数,设置一个utc时间
payload['exp'] = expiry
token = jwt.encode(payload, secret, algorithm='HS256')
return token
from datetime import datetime,timedelta
# datetime.utcnow() + timedelta(hours=2) 当前的utc时间 + 2个小时 有效期在两个小时后过期
xx = general_jwt({'name': 123}, datetime.utcnow() + timedelta(hours=2) , '123')
def verify_jwt(token, secret=None):
'''
验签
:param token: 返回的值
:param secret: 密钥
:return: 返回解密的后的内容
'''
try:
payload = jwt.decode(token, secret, algorithms=['HS256'])
except jwt.PyJWTError: # 验证失败后的异常
payload = None
return payload
m = verify_jwt(xx, '123')
print(m) # {'name': 123, 'exp': 1651502322}
生成token和验证token
生成token :
头部 生成一个base64编码 -> 载荷payload生成一个base64为的编码 ->通过数学计算获取到token值
# 在jwt生成token时会使用secret密钥作为验证
# 载荷部分存储这用户的id等等 和exp设置的过期时间
例如:
是由: head头部. payload载荷部分 .计算部分
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoxMjMsImV4cCI6MTY1MTUwMjMyMn0.ByqnK783wVHWHVqX4umnPN-ic09bJTitODOQnVt1nBU
验签部分:
head头部. payload载荷部分.计算部分 # 进行hs256计算
base64编码 base64编码
# 计算的部分要与secret密钥字符串进行比较
如果相同那么jwt有效,才会将载荷部分的内容返回,在进行查看exp中有有效期是否过期过期那么token值就是none
token刷新机制
# 正常情况下的token请求的步骤
客户端 --------------- 服务器
1.客户端发送请求进行登录
2.服务端验证登录的密码账号,成功返回一个token值
3.客户端(前端保存token)
4.当客户端访问其他的服务器页面时,会在header头部携带token值
5.服务器进行验签如果携带token正确就进行返回正常视图
没有携带返回401
token有问题 返回401/403
# 用户无感知情况下获取token值
# 解决token刷新问题
1. token的有效期: 设置为2个小时,在接口调用携带,每隔2个小时进行刷新一次
2. 提供refresh_token接口 获取新的token,有效期14天
3.在接口调用token过期后凭证借refresh_token 获取新的token
4.没有携带token,错误token或者调用接口token过期 返回401状态码
5.refresh_token过期返回403状态码,前端使用refresh_token请求新的token是遇到403状态码则进入用户的新的认证页面
6.token携带方式在请求头的格式
Authorization:jwt jwt码
# 带有自动刷新下的token认证机制 流程
1.当登录时验证密码和账号,返回2个token(1.正常的请求token 2个小时过期,2.刷新的token 14天过期)
第一种情况:
当请求正常条件下
1.token没有问题 正常访问
2.token没有携带 返回401 返回登录页面正常登录
3.token过期 返回403
当非正常情况下
token过期的时候,请求头就会携带这个 '刷新的token' 请求'refresh_token刷新的接口' 服务器进行验证这个 '刷新的token'
不正确的情况下返回 401 重新登录
正确的情况下,这个接口就会返回一个新的'访问token',请求头内就存放着访问的 新的token,获取请求的正常逻辑。
刷新的实现
# post 请求 /authorization
1.登录的接口设计:
1.在登录是需要发送post请求
{ # 将密码和账号发送后后端服务器,以json格式发送
name:'',
paw:''
}
2.在后端接受完成返回错误返回400
正确: 返回状态码,和token,刷新的token内容
{
code:200,
'message':'ok',
data:{
'token':'',
"refresh_token":''
}
}
前端需要将data中的值存储到前端vue中的会话技术中localstorage
2.接口的代码设计:
在flaks api框架获取用户的密码和账号的时候,就直接进行数据库或者其他到的逻辑判断,证明这个用户时可以登录,在登录的时候,需要携带token值返回给前端
# put 请求 /authorization
刷新token的接口也是
put 进行请求,刷新的token也是在请求头中存储的
请求头中:put / Authorization: bearer refres_token
返回结果
错误 返回403 重新登录
正确返回
{
'message':'ok',
'data':{
'token':'请求token'
}
}
jwt的禁用的问题
在服务器中开始保存数据
场景:
1.当用户在 ios 安卓 移动web端登录 在ios端进行 密码修改,
2. 对不良用户禁用
实现的思路:
1.使用redis进行保存
2. 使用redis的list 或者set 优先使用set 比较的速度比list的快
时间复杂度比较快
假设用户修改了密码,但是用户有多个平台登录,需要禁用当前用户的其他平台的token值。
技术栈:
1.redis数据 数据结构为set(o(1))
# 当用户修改密码或者对用户进行禁用代码
key = 'user:{'%s'}:token' % user_id # 存储的键
pl = redis.pipeline() # 事务 管道
pl.sadd(key,new_token) # 修改密码 后的新token值
pl.expire(key,token有效期) # 给这key设置有效期,token的有效期就是新的生成的token的有效期,等待其他平台老token死掉就可以了
pl.execute() # redis 提交事务
# 校验时
1.用户使用token进行请求 时,如果校验token通过,从redis中判断当前用户是否存在记录'user:{'%s'}:token' % user_id
没有记录:放行,进行视图业务
有记录:对本次请求 的token是否存在redis中set中
存在:放行
不存在:返回403状态码,不在处理业务逻辑
key = 'user:{'%s'}:token' % user_id
valid_tokens = redis.smembers(key,token) # 取白名单
if valid_tokens and token not in valid_tokens:
# 白名单存在,并且这个token如果不在白名单中返回403
return {"message":'token非法'},403
补充说明:
redis记录设置有效时间长是一个token的有效期,保证旧token过期后,redis的记录也要删除,不占用空间
使用set保存新的token的原因,烤炉用户可能在旧token的有效期内容在多设备登录,需要生成多个新的token,这些token都要保存下来,保存了新token的正常登录,又能保证旧token被禁用
案例
from datetime import datetime, timedelta
import jwt
from flask import Flask, g, request
from flask_restful import Api, Resource # 导入类
from flask_restful.reqparse import RequestParser
app = Flask(__name__)
api = Api(app)
SECRET = '这是一个密钥'
# 1.生成的jwt方法
def generate_jwt(payload, expiry, secret=None):
'''
利用jwt方法生成token值
:param payload: 载荷 也就是需要加密的token值
:param expiry: 有效期 utc时间
:param secret: 密钥
:return: 返回token值
'''
_payload = {'exp': expiry}
_payload.update(payload)
token = jwt.encode(_payload, secret, algorithm='HS256')
return token
# 2.验证jwt方法
def verify_jwt(token, secret):
'''
验签token值
:param token: token值
:param secret: 密钥
:return: 验签后的载荷
'''
try:
payload = jwt.decode(token, secret, algorithms=['HS256'])
except jwt.PyJWTError: # 如果载荷过期,那么就会执行这个异常方法
payload = None
return payload
# 1.创建一个请求 钩子(flask的中间件) 每次请求都会执行这个钩子函数,获取请求头中token值进行判断并且赋值给g对象
@app.before_request
def jwt_authentication():
'''
flask的钩子函数
1.获取请求头中的token,因为存在两个请求头,所以需要进行识别
2.进行判断token值的是否存在
3.验签
4.存入到flask对象中的g对象中
:return:
'''
token = request.headers.get('Authorization') # 获取请求头中的token值
# 设置初始默认值
g.user_id = None
g.is_refresh = None
if token is not None and token.startswith('JWT '): # 一个开头的就是当前的请求token
token = token.split(' ')[1] # 获取
# 验证当前的token值
payload = verify_jwt(token, SECRET)
if payload is not None:
# 当获取的载荷payload不为空,那么没有过期可以使用
g.user_id = payload.get('user_id') # 获取id
g.is_refresh = payload.get('is_refresh', False) # 因为刷线的token中存储了is_refresh值,进行获取,如果没有默认为false,不是刷新token
# 1. 装饰器:作用:在每一次请求进入视图方法中时进行一次判断,判读当前的钩子方法中解析出来的g对象,是请求的token值否
def login_required(func):
def wrapper(*args, **kwargs):
# 进行判断,请求携带的token必须是有效的不能是刷新的token
if g.user_id is not None and g.is_refresh is False:
# user_id 必须有值,并且 is_refresh属性必须为false(false就代表当前是正常的token)
return func(*args, **kwargs)
else:
return {'msg': 'token非法'}, 401
return wrapper
# api类
class IndexLogin(Resource):
# 给当前的视图中的方法装饰装饰器
method_decorators = {
'post': [login_required, ],
}
def __init__(self):
self.expiry = datetime.utcnow() # 因为jwt过期时间使用的utc时间
# 3.生成token值
def _generate_tokens(self, user_id, refresh=False):
'''
:param user_id: 登录后用户的id
:param refresh: 根据这个值,需要请求token还是不需要请求token
:return: 刷新token和请求token
'''
'''这个方法主要是在用户登录时是生成token'''
# 生成请求token 有效期2个小时
expiry = self.expiry + timedelta(hours=2)
token = generate_jwt({'user_id': user_id}, expiry, SECRET)
# 生成刷新token,作用在请求token过期是,需要将刷新token请求刷线接口访问获取新的请求token
if refresh:
# 刷线token需要14天
expiry = self.expiry + timedelta(days=14)
# 'is_refresh': True 代表当前的token是刷新token
refresh_token = generate_jwt({'user_id': user_id, 'is_refresh': True}, expiry, SECRET)
else:
refresh_token = None
return token, refresh_token
def post(self):
'''用户post请求,登录时返回token值,每次访问时,需要在请求头中携带这个token'''
user_id = None
rp = RequestParser() # 获取参数 可以获取请求头中的参数也可以获取查询参数,请求体参数
rp.add_argument('name')
rp.add_argument('pwd')
res = rp.parse_args() # 校验
if res.get('name') == 'wkx' and res.get('pwd') == '123':
user_id = 1
token, refresh_token = self._generate_tokens(user_id, True)
return {
'code': 200, 'data': {
'token': token,
'refresh_token': refresh_token
},
'msg': '登录成功'
}
def put(self):
'''
在请求token过期后,请访问这个接口的put方法,获取新的请求token
而前端需要将刷新的token放在请求头中
:return:
'''
if g.user_id is not None and g.is_refresh is True:
token, refresh_token = self._generate_tokens(g.user_id)
return {'msg': '这是新的token值', 'data': {
'token': token,
}}
return {'msg': '这个接口负责给前端新的token值'}
api.add_resource(IndexLogin, '/api/v1/') # 将视图类与api进行捆绑
if __name__ == '__main__':
app.run()
'''
前端需要在请求头中携带参数
Authorization:
JWT 这个是一个标志
# token值
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
eyJleHAiOjE2NTE2NzU5NTcsInVzZXJfaWQiOm51bGx9.
YkwufLfsOZXSKbfx3-IREENE8InBXrewXZa3ogrOxMM
'''
Faker生成测试数据
# 中文网址:
https://faker.readthedocs.io/en/master/locales/zh_CN.html
# 用于生成测试数据模块
pip install Faker
1.通过Faker设置模拟数据(以终端命令的情况下设置)
表
import datetime
# 创建一张表 学生
class Student(db.Model):
__tablename__ = 'student'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(15), index=True, comment='学生名称')
age = db.Column(db.SmallInteger, comment='年龄')
sex = db.Column(db.Boolean, default=True, comment='性别')
email = db.Column(db.String(128), comment='邮箱')
created_time = db.Column(db.DateTime, default=datetime.datetime.now, comment='创建时间')
money = db.Column(db.Numeric(10, 2), default=0.0, comment='钱包')
def __repr__(self):
return f'<{self.name} - {self.__class__.__name__}>'
2.自定义终端命令
'''基于faker生成仿真的模拟数据'''
import random, click
from faker import Faker
fakers = Faker(locale='ZH_CN')
@app.cli.command('faker_user')
@click.argument('num', default=10, type=int) # 终端命令
def faker_user_command(num):
st_list = []
for i in range(num):
sex = bool(random.randint(0, 1))
st = Student(
name=fakers.name_male() if sex else fakers.name_female(),
age=random.randint(15, 60),
sex=sex,
email=fakers.free_email(),
money=float(random.randint(100, 100000) / 100),
created_time=fakers.date_time()
)
st_list.append(st)
db.session.add_all(st_list)
db.session.commit()
需要设置 FLASKE_APP=app.py
flask faker_user 在终端启动生成随机数据
案例
进度条打印
import time
# res = ''
# for i in range(50):
# # T
# res += '#'
# time.sleep(0.5)
# print('\r[%-50s]' % res, end='')
# end='' 不换行的意思
# \r 回车的意思从头开始
# 也就是[%s] 普通的占位符
# [%50s] # 是这样的效果[ ########] 这列表的长度为站位50
# [%-s] # [####### ] 从左边开始
# [%-50s] 意思就是 左对齐 宽度50 -左对齐 50是宽度
# 模拟下载显示进度条
recv_size = 0 # 已经下载的字节长度
total_size = 10250 # 模拟下载的长度
while recv_size < total_size:
time.sleep(0.3) # 模拟下载带宽
recv_size += 1024 # 将字节添加,这个字节的大小需要根据剩余下载的数量绝对
# 获取已下载的长度和总长度的百分比
percent = recv_size / total_size # 使用已下载的字节 / 总字节 下载的百分比
if percent > 1:
percent = 1
res = int(percent * 50) * '#' # 获取的下载进度条的长度 的#号 根据百分比来的percent
int_num = percent * 100 # 获取百分比进行进度条片的显示
print('\r[%-50s] %d%%' % (res, int_num), end='') # 显示进度条 与百分比
猴子补丁操作
使用的模块如果不满意某些功能,可以进行对方法进行打补丁,修改为自己的代码
补丁的导入:在首次导入的位置进行打入补丁,后续在进行导入的位置就发生了变化
在入口文件进行导入包后进行打补丁
# 思想:把源代码替换自己功能的操作应该放到文件的入口(启动文件)程序无论其他的位置在导,就会使用到自己的功能操作
不用了:就在入口文件删除了就行,或者替换
例如:
ujson 比json 的效率高
ujson 的dumps 高于json的效率
ujson 的loads 高于json的效率
import json
import ujson
# 方法替换
json.dumps = ujson.dumps # 替换为ujson
json.loads = ujson.loads # 替换为ujson
# 在入口文件进行替换执行
def mokey_patch_json():
'''只是将json名称空间的方法进行替换了'''
json.__name__ = 'ujson' # 将模块名替换
json.dumps = ujson.dumps # 替换为ujson 的方法
json.loads = ujson.loads # 替换为ujson 的方法
mokey_patch_json() # 直接执行
# 如果对哪些包中方法不合适,就可以在入口文件中进行替换为自己的方法或者新的方法
# 打补丁就是将某些方法给替换,不想用可以进行注释掉就行,就会变为原来的方法
Python虚拟环境
pip批量卸载安装
pip uninstall -r requirements.txt
pip install -r requirements.txt
virtualenv包
虚拟环境Linux
linux and 本地:
pip install virtualenv
1.创建
virtualenv -p python3(使用的环境) 环境名称
在linux中需要创建软连接(要不不就是用绝对路径)
find / -name virtualenv
ln -s /usr/local/python3/bin/virtualenv /usr/bin/virtualenv
创建后的虚拟环境在当前所在目录下
2.生效
source 虚拟环境的文件名/bin/activate
例如:
source blogweb/bin/activate
(blogweb) [root@VM-12-16-centos python39] 在当前环境下 全部的pip下载都在当前环境下 直接批量导入就可以
3.启动项目
python3 manage.py startproect 端口与ip
需要修改django中的配置参数才可以外部访问
4.退出
deactivate
虚拟环境win
1.虚拟环境包
pip install virtualenv
# 当前电脑中的D:\python_virtualenv 文件夹中存放虚拟环境 进入指定的目录文件夹,创建虚拟环境
2.创建虚拟环境
virtualenv 虚拟环境名称 --python=python3.9 # 指定python包
virtualenv 虚拟环境名称 --python=python的路径 # 指定python包
3.激活虚拟环境
进入当前目录创建虚拟环境的目录
例如
D:\python_virtualenv\interact\Scripts
scripts文件夹下
执行
进行对虚拟环境的激活 相当于进入虚拟环境
activate
退出虚拟环境
deactivate
4.虚拟环境安装模块
先激活 在安装
进入虚拟环境中进行安装
pip install django==3.2 版本
5.怎么使用虚拟环境
1.在pc中进行新建项目
2.点击使用virtualenv虚拟环境 中的先前配置的解释器,
3.点击添加解释器,找到虚拟环境下的文件夹目录
4.Scripts\python.exe 进行配置虚拟环境
不创建的情况下
1.点击解释器中的文件,中的设置
2.项目下的python解释器
3.添加新的解释器,找到虚拟环境的文件夹
4.Scripts\python.exe 进行配置虚拟环境
需要安装什么包就在终端中进行安装
(interact) C:\Users\56515\Desktop\interact> 默认pc中使用的就是虚拟环境
venv包虚拟环境
1.创建虚拟环境
python -m venv py_venv
# 会创建在当前项目下的相对路径
2.linux环境下激活虚拟环境
source py_venv/bin/activate
3.linux环境下退出虚拟环境
py_venv/bin/deactivate
4.win环境下
py_venv/bin/activate # 进入
py_venv/bin/deactivate # 退出