在python的三种时间表现形式:
1.时间戳: 给电脑看的。
- 自格林威治时间 1970-01-01 00:00:00 到当前时间,按秒计算,计算到当前时间总共用了多少秒。2.格式化时间(Format String): 给人看的
- 返回的是时间的字符串 2015-12-123.格式化时间对象(struct_time):
- 返回的是一个元组, 元组中有9个值:
9个值分别代表: 年、月、日、时、分、秒、一周中第几天,一年中的第几天,夏令时(了解)
import time
1、获取时间戳 (计算时间时使用)
print(time.time())
2、获取格式化时间(拼接用户时间格式并保存时使用)
print(time.strftime('%Y-%m-%d')) #获取年月日
print(time.strftime('%Y-%m-%d %H:%M:%S')) #获取年月日时分秒
3、获取时间对象
print(time.localtime()) #返回的时一个元组
print(type(time.localtime())) #
time_obj = time.localtime()
print(time_obj.tm_year) #2019
print(time_obj.tm_mon)
3.1、获取当前时间的格式化时间
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
# res = time.localtime()
# time.sleep(5)
3.2、获取当前时间的格式化时间
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
3.3、将时间对象转为格式化时间
print(time.strftime('%Y-%m-%d %H:%M:%S', res))
3.4、将字符串格式的时间转为时间对象
res = time.strptime('2019-01-01', '%Y-%m-%d')
print(res)
import datetime
获取当前年月日
print(datetime.date.today()) #2019-11-16
获取当前年月日时分秒
print(datetime.datetime.today()) #2019-11-16 14:48:18.744359
#time_obj = datetime.datetime.today()
#print(type(time_obj)) #
#print(time_obj.year)
#print(time_obj.month)
#print(time_obj.day)
'''
日期/时间的计算
新日期时间 = 旧日期时间 “+” or “-” 时间对象
时间对象 = 新日期时间 “+” or “-” 旧日期时间
'''
旧日期时间:
current_time = datetime.datetime.now()
print(current_time)
时间对象
time_obj = datetime.timedelta(days=7) #获取7天时间
print(time_obj)
新日期时间 = 旧日期时间 “+” or “-” 时间对象
later_time = current_time + time_obj
print(later_time)
时间对象 = 新日期时间 “+” or “-” 旧日期时间
time_new_obj = later_time - current_time
print(time_new_obj)
import random
1.随机获取x—n中任意的整数
res = random.randint(x, n)
print(res)
2.默认获取0——1之间任意小数
res2 = random.random()
print(res2)
# 洗牌
3.将可迭代中的值乱序排列
-list
list1 = ['红桃A', '梅花A', '红桃Q', '方块K']
random.shuffle(list1)
print(list1)
4.随机获取可迭代对象中的某一个值 注意:有索引的可迭代对象
list1 = ['红桃A', '梅花A', '红桃Q', '方块K']
res3 = random.choice(list1)
print(res3)
5.从指定序列中随机获取指定长度的片断。sample函数不会修改原有序列。
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
slice = random.sample(list1, 5) #从list中随机获取5个元素,作为一个片断返回
print(slice)
>>>[3, 8, 5, 10, 4]
6、随机获取1-9的浮点数
random.uniform(1,9)
案例:随机验证码的生成
'''
需求:
大小写字母、数字组合而成
组合5位数的随机验证码
前置技术:
- chr(97) # 可以将ASCII表中值转换成对应的字符
# print(chr(101))
- random.choice
'''
#获取任意长度的随机验证码
def get_code(n):
code = ''
#每次循环只从大小写字母,数字中取出一个字符
#for line in range(5):
for line in range(n):
#随机获取一个小写字母
res1 = random.randint(97,122)
lower_str = chr(res1)
#随机获取一个大写字母
res2 = random.randint(65,90)
upper_str = chr(res2)
#随机获取一个数字
number = str(random.randint(0,9))
#获取每一个字符的三种形式放入一个列表
code_list = [lower_str,upper_str,number] #['g', 'S', '4']
#随机取出每一个字符
random_code = random.choice(code_list)
#字符串拼接
code = code + random_code
return code
code = get_code(n)
print(code)
os模块是与操作系统交互的模块
import os
需求:
1.获取当前文件中的上一级目录
DAY15_PATH = os.path.dirname(__file__)
print(DAY15_PATH)
2.项目的根目录,路径相关的值都用 “常量”
BASE_PATH = os.path.dirname(DAY15_PATH)
print(BASE_PATH)
3.路径的拼接: 拼接文件 “绝对路径”
TEST_PATH = os.path.join(DAY15_PATH,'模拟1.txt')
print(TEST_PATH)
4.判断文件/文件夹”是否存在:若文件存在返回True,若不存在返回False
print(os.path.exists(TEST_PATH))
print(os.path.exists(DAY15_PATH))
5.判断文件是否存在
os.path.isfile()
6.判断“文件夹”是否存在
print(os.path.isdir(TEST_PATH))
print(os.path.isdir(DAY15_PATH))
7.创建文件夹
os.mkdir(DIR_PATH) #只传文件夹名字,会保存在当前执行文件的目录内
8.删除文件夹: 只能删除 “空的文件夹”
os.rmdir(DIR_PATH)
9.获取某个文件夹中所有文件的名字
list1 =os.listdir(r'F:\python正式课\day15')
print(list1)
需求:打印用户选择的文件内容
list1 =os.listdir(r'F:\python正式课\day15\模拟文件夹')
print(list1)
# enumerate(可迭代对象) #返回一个enumerate对象,对象里是一个个的元组(索引, 元素) #注意:可迭代对象中若是字典,则取字典中所有的key
res = enumerate(list1)
print(list(res))
#让用户选择文件
while True:
#打印所有文件的名字
for index,name in enumerate(list1):
print(f'编号:{index} 文件名:{name}')
choice = input('请选择想看的老师作品 编号:').strip()
#限制用户必须输入数字,数字的范围必须在编号内
#若不是数字,则重新选择
if not choice.isdigit():
print('必须输入数字')
continue
#若是数字,往下走判断是否在编号范围内
#若不在列表范围内,则重新选择
choice = int(choice)
if choice not in range(len(list1)):
print('编号范围错误')
continue
file_name = list1[choice]
list1_path = os.path.join(r'F:\python正式课\day15\模拟文件夹',file_name)
print(list1_path)
with open(list1_path, 'r', encoding='utf-8') as f:
print(f.read())
1、sys.argv:获取操作系统中cmd(终端)的命令行 python3 py文件 用户名 密码
import sys
print(sys.argv) 返回的是列表['py文件', '用户名', '密码']
cmd_list = sys.argv
#执行文件权限认证
if cmd_list[1] == 'bob' and cmd_list[2] == '123':
print('通过验证')
print('开始执行逻辑代码')
else:
print('用户名或密码错误,权限不足!')
2、sys.path:获取当前的Python解释器的环境变量路径
print(sys.path)
将当前项目添加到环境变量中
BASE_PATH = os.path.dirname(os.path.dirname(__file__))
#print(BASE_PATH)
sys.path.append(BASE_PATH)
print(sys.path)
hashlib是一个加密模块:内置了很多算法
MD5: 不可解密的算法(2018年以前)
摘要算法:
- 摘要是从某个内容中获取的加密字符串
- 摘要一样,内容就一定一样: 保证唯一性
- 密文密码就是一个摘要
import hashlib
md5_obj = hashlib.md5()
# print(type(md5_obj)) #
str1 = '1234'
bytes1 = str1.encode('utf-8') #把字符串转成bytes类型
# print(bytes1) # b'1234'
md5_obj.update(bytes1) # update中一定要传入bytes类型数据
res = md5_obj.hexdigest() #得到一个加密后的字符串
print(res) # 81dc9bdb52d04dc20036dbd8313ed055
以上操作有一个问题,就是撞库。撞库有可能会破解真实密码
防止撞库问题:加盐:
import hashlib
def pwd_md5(pwd):
md5_obj = hashlib.md5()
str1 = pwd #pwd ='1234'
md5_obj.update(str1.encode('utf-8'))
#创造盐
sal ='我好帅'
#加盐
md5_obj.update(sal.encode('utf-8'))
res = md5_obj.hexdigest() #得到一个加密后的字符串 4ccef78b05804b0890e47bc0d045c236
return res
#模拟用户登录操作
#获取文件中的用户名与密码
with open('user.txt', 'r', encoding='utf-8') as f:
user_str = f.read()
file_user, file_pwd = user_str.split(':')
# 用户输入用户名与密码
username = input('请输入用户名:').strip()
password = input('请输入密码:').strip()
# 校验用户名与密码是否一致
if username == file_user and file_pwd == pwd_md5(password): # pwd_md5('1234')
print('登陆成功')
else:
print('登陆失败')
序列化: 将Python或其他语言的数据类型转换成字符串类型。
- int, float, str, list, tuple, dict, bool, set
list1 = [1, 2, 3] -----> "[1, 2, 3]"
序列: 指的是字符串。
序列化: serializable
其他数据类型 ----> 字符串 ----> 文件中
反序列化:
文件中 ---> 字符串 ---> 其他数据类型
- json
第三方的
- pickle
属于python的
1、什么是json模块
json模块:是一个序列化模块
json:
是一个“第三方"的特殊数据格式。
可以将python数据类型 ----》 json数据格式 ----》 字符串 ----》 文件中
其他语言要想使用python的数据:
文件中的 ----》 字符串 ----》 json数据格式 ----》 其他语言的数据类型。
注意:
-在json中,所有的字符串都是双引号
-元组比较特殊: python中的元组,若将其转换成json数据,内部会将元组转化成列表
-set不能转换成json数据
2、为什么要使用json
-为了让不同的语言之间数据可以共享。
由于各种语言的数据类型不一,但长相可以一样,比如python不能直接使用其他语言的数据类型,
必须将其他语言的数据类型转换成json数据格式, python获取到json数据后可以将其转换成python的数据类型。
3、如何使用json
import json
json模块的内置方法:
- json.dumps:
f = open() --> f.write()
# 序列化: python数据类型 ---》 json ---》 字符串 ---》 json文件中
- json.loads:
f = open(), str = f.read(), json.loads(str)
# 反序列化: json文件中 --》 字符串 ---》 json ---》 python或其他语言数据类型
- json.dump(): # 序列化: python数据类型 ---》 json ---》 字符串 ---》 json文件中
- 内部实现 f.write()
- json.load(): # 反序列化: json文件中 --》 字符串 ---》 json ---》 python或其他语言数据类型
- 内部实现 f.read()
- dump, load: 使用更方便
注意: 保存json数据时,用.json作为文件的后缀名
import json
dumps,loads的用法:
#列表
list1 = ['张全蛋','李小花']
#ensure_ascii 将默认的ascii取消设置为False,可以在控制台看到中文,否则看到的是bytes类型数据
json_str = json.dumps(list1,ensure_ascii = False)
print(json_str)
print(type(json_str)) #
python_date = json.loads(json_str)
print(python_date)
print(type(python_date)) #
#元组
tuple1 =('张全蛋','李大花')
json_str = json.dumps(tuple1,ensure_ascii = False)
print(json_str)
print(type(json_str))
python_date = json.loads(json_str)
print(python_date)
print(type(python_date)) #
#字典
dic = {
'name': 'tank',
'age': 17
}
json_str = json.dumps(dic,ensure_ascii = False)
print(json_str) #str
python_date = json.loads(json_str)
print(python_date)
print(type(python_date)) # dict
#注册功能
def register():
username = input('请输入用户名:').strip()
password = input('请输入密码:').strip()
re_password = input('请确认密码:').strip()
if password == re_password:
user_dic = {'name': username, 'pwd': password} # {'name': username, 'pwd': password}
json_str = json.dumps(user_dic, ensure_ascii=False)
#开始写入文件中
# 注意: 保存json数据时,用.json作为文件的后缀名
with open('user.json','w',encoding = 'utf-8') as f:
f.write(json_str)
register()
dump,load的用法:
user_dic = {'username': '张全蛋','password': 123}
with open('user3.json', 'w', encoding='utf-8') as f:
json.dump(user_dic, f)
with open('user3.json', 'r', encoding='utf-8') as f:
json.load(f)
pickle是一个python自带的序列化模块
优点:
可以支持python中所有的数据类型
可以直接存 “bytes类型” 的数据,pickle存取速度更快
缺点: (致命的缺点)
只能支持python使用,不能跨平台
import pickle
set1 = {
'tank', 'sean', 'jason', '大脸'
}
# 存 dump
with open('teacher.pickle', 'wb') as f:
pickle.dump(set1, f)
# 取 load
with open('teacher.pickle', 'rb') as f:
#python_set = pickle.load(f)
#print(python_set)
#print(type(python_set))
提供一些python八大数据类型 “以外的数据类型” 。
python默认八大数据:
1、具名元组:
具名元组 只是一个名字。
应用场景:
- 坐标
from collections import namedtuple
2、 有序字典:
- python中字典默认是无序
- collections中提供了有序的字典
from collections import OrderedDict
from collections import namedtuple
'''
传入可迭代对象是有序的
应用:坐标
将'坐标'变成 “对象” 的名字
'''
point = namedtuple('坐标', ['x', 'y']) # 第二个参数可以传可迭代对象
# point = namedtuple('坐标', ('x', 'y')) # 第二个参数可以传可迭代对象
# point = namedtuple('坐标', 'x y') # 第二个参数可以传可迭代对象
#传参的个数,要与namedtuple第二个参数的个数一一对应
p = point(1, 2) # 会将 1 ---> x, 2 ---> y
print(p) #坐标(x=1, y=2)
# print(type(p)) #
python默认无序字典:
dic = dict({'x': 1, 'y': 2, 'z': 3})
print(dic) #{'x': 1, 'y': 2, 'z': 3}
print(type(dic)) #
有序字典:
from collections import OrderedDict
order_dict = OrderedDict({'x': 1, 'y': 2, 'z': 3})
print(order_dict, '打印有序的字典') #OrderedDict([('x', 1), ('y', 2), ('z', 3)])
print(type(order_dict)) #
print(order_dict['y'])
for line in order_dict:
print(line)
它是一个第三方模块
- 可以对Excle表格进行操作的模块
- Excel版本:
2003之前:
excle名字.xls
2003以后:
excle名字.xlsx
- 清华源: https://pypi.tuna.tsinghua.edu.cn/simple
- 配置永久第三方源:
D:\Python36\Lib\site-packages\pip\_internal\models\index.py
写入数据:
from openpyxl import Workbook
# 获取Excel文件对象
wb_obj = Workbook()
wb1 = wb_obj.create_sheet('python工作表1', 1)
wb2 = wb_obj.create_sheet('python工作表2', 2)
# 修改工作表名字: 为 python工作表1 标题修改名字 ---》 大宝贝
print(wb1.title)
wb1.title = '大宝贝'
print(wb1.title)
# 为第一张工作表添加值
# 用法:wb1['工作簿中的表格位置']
wb1['A10'] = 200
wb1['B10'] = 1000
wb1['C10'] = '=SUM(A10:B10)'
#生成Excel表格
wb_obj.save('python.xlsx')
读取数据:
from openpyxl import load_workbook
wb_obj = load_workbook('python.xlsx')
wb1 = wb_obj['大宝贝'] # wb_obj['表名']
print(wb1['A10'].value)
wb1['A10'] = 20
print(wb1['A10'].value)
需求:批量写入100条数据
from openpyxl import Workbook
wb_obj = Workbook()
wb1 = wb_obj.create_sheet('工作表1')
#wb1['表格位置'] = 对应的值
n=1
for line in range(100):
wb1['A%s'% n] = line+1
n += 1
wb_obj.save('工作表.xlsx')
print('excel表格生成成功')
用途:可以通过python代码给操作系统终端发送命令,并且可以返回结果。
import subprocess
while True:
cmd_str = input('请输入终端命令:').strip()
# Popen(cmd命令, shell=True,
# stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# 调用Popen就会将用户的终端命令发送给本地操作系统的终端
# 得到一个对象,对象中包含着正确或错误的结果。
obj = subprocess.Popen(
cmd_str,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
success = obj.stdout.read().decode('gbk')
if success:
print(success,'正确的结果')
error = obj.stderr.read().decode('gbk')
if error:
print(error,'错误的结果')
logging 模块是用来记录日志的模块,一般记录用户在软件中的操作。
import os
import logging.config
#定义三种日志输出格式 开始
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
'[%(levelname)s][%(message)s]' #其中name为getlogger指定的名字
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'
#定义日志输出格式 结束
注意1:log文件的目录
BASE_PATH = os.path.dirname(os.path.dirname(__file__))
logfile_dir = os.path.join(BASE_PATH,'log_dir')
print(logfile_dir)
注意2:log文件名
logfile_name = 'use.log'
#如果不存在定义的日志目录就创建一个
if not os.path.isdir(logfile_dir):
os.mkdir(logfile_dir)
#log文件的全路径
logfile_path = os.path.join(logfile_dir,logfile_name)
# print(logfile_path)
注意3:log配置字典
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', # 日志文件的编码,再也不用担心中文log乱码了
},
},
'loggers': {
#logging.getLogger(__name__)拿到的logger配置
'': {
'handlers': ['default', 'console'], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
'level': 'DEBUG',
'propagate': True, # 向上(更高level的logger)传递
},
},
}
注意4:
def get_logger(user_type):
# 1.加载log配置字典到logging模块的配置中
logging.config.dictConfig(LOGGING_DIC)
# 2.获取日志对象
logger = logging.getLogger(user_type)
return logger
#通过logger日志对象,调用内部的日志打印
logger = get_logger('user')
#记录日志信息
logger.info('只要思想不滑坡,方法总比问题多!')
正则表达式:
正则表达式是一门独立的技术, 任何语言都可以使用正则表达式,
正则表达式是由一堆特殊的字符组合而来的。
- 字符组
- 元字符
- 组合使用
在python中,若想使用正则表达式,必须通过re模块来实现。
比如要获取“一堆字符串”中的“某些字符”,正则表达式可以帮我们过滤,并提取出想要的字符数据。
比如过滤并获取 “tank”
'gstankyr9q
- 应用场景:
– 爬虫: re, BeautifulSoup4, Xpath, selector
– 数据分析过滤数据: re, pandas, numpy…
–用户名与密码、手机认证:检测输入内容的合法性
import re
需求:检测手机号码的合法性: 要求11位、以13/14/15/19开头的手机号码
# 纯python校验
while True:
phone_number = input('请输入手机号码:').strip()
# not > and > or
if len(phone_number) == 11 and (phone_number.startswith(
'13'
) or phone_number.startswith(
'14'
) or phone_number.startswith(
'15'
) or phone_number.startswith(
'19'
)):
print('手机号码合法!')
break
else:
print('手机号码不合法!')
#re校验
import re
'''
参数1: 正则表达式 ''
参数2: 需要过滤的字符串
# ^: 代表“开头”
# $: 代表“结束”
# |: 代表“或”
# (13|14): 可以获取一个值,判断是否是13或14.
# {1}: 需要获取1个值 限制数量
# []: 分组限制取值范围
# [0-9]: 限制只能获取0——9的某一个字符
'''
while True:
phone_number = input('请输入手机号码:').strip()
if re.match('^(13|14|15|19)[0-9]{9}$', phone_number):
print('合法')
break
else:
print('不合法')
字符组:
- [0-9] 可以匹配到一个0-9的字符
- [9-0]: 报错, 必须从小到大
- [a-z]: 从小写的a-z
- [A-Z]: 从大写A-Z
- [z-A]: 错误, 只能从小到大,根据ascii表来匹配大小。
- [A-z]: 总大写的A到小写的z。
注意: 顺序必须要按照ASCII码数值的顺序编写。
元字符:
https://images2015.cnblogs.com/blog/1036857/201705/1036857-20170529203214461-666088398.png
- 组合使用
- \w\W: 匹配字母数字下划线与非字母数字下划线,匹配所有。
- \d\D: 无论是数字或者非数字都可以匹配。
- \t: table
- \n: 换行
- \b: 匹配单词结尾,tank jasonk
- ^: startswith
- '^'在外面使用: 表示开头。
- [^]: 表示取反的意思。
- $: endswith
- ^$: 配合使用叫做精准匹配,如何限制一个字符串的长度或者内容。
- |: 或。ab|abc如果第一个条件成立,则abc不会执行,怎么解决,针对这种情况把长的写在前面就好了,一定要将长的放在前面。
- [^...]: 表示取反的意思。
- [^ab]: 代表只去ab以外的字符。
- [^a-z]: 取a-z以外的字符。
1、re.findall():可以匹配 "所有字符" ,拿到返回的结果,返回的结果是一个列表。
'afwaiahoaw' # a
['a', 'a', 'a', 'a']
2、re.search():在匹配一个字符成功后,拿到结果后结束,不往后匹配。
----> obj ----> obj.group()
'afwaiahoaw' # a
'a'
3、re.match():从匹配字符的 开头 匹配,若开头不是想要的内容,则返回None。
----> obj ----> obj.group()
'afwaiahoaw' # a
'a'
'wfaghoio' # a
None
'''
#findall
import re
str1 = 'sean tank json'
res = re.findall('[a-z]{3}',str1)
print(res)
>>>['sea', 'tan', 'jso']
#search
res = re.search('[a-z]{4}',str1)
#print(res)
print(res.group())
>>>sean
#march
res =re.match('sean',str1)
# print(res.group()) #报错
if res:
print(res.group()) #sean
example = "abbbbbbc"
pattern = re.compile("ab+")
贪婪模式:正则表达式一般趋向于最大长度匹配,也就是所谓的贪婪匹配。
如上面使用模式pattern 匹配字符串example,匹配到的结果就是”abbbbbb”整个字符串。
非贪婪模式:在整个表达式匹配成功的前提下,尽可能少的匹配。
如上面使用模式pattern 匹配字符串example,匹配到的结果就只是”ab”整个字符串。
所以,我们可以将贪婪模式理解为:在整个表达式匹配成功的前提下,尽可能多的匹配。
将非贪婪模式理解为:在整个表达式匹配成功的前提下,尽可能少的匹配。
print(re.findall('^egon$','egon')) #^xxx$ 开头找,倒着找只能找egon,多一点少一点都不行
print(re.findall('a\nc','a\nc a\tc a1c')) # ['a\nc']
print(re.findall('a\tc','a\nc a\tc a1c')) # ['a\tc']
重复匹配:
. ? * + {m,n} .* .*?
1、. :代表除了换行符之外的任意一个字符
print(re.findall('a.c','abc a1c aAc aaaaac')) #['abc', 'a1c', 'aAc', 'aac']
print(re.findall('a.c','abc a1c aAc aaaaa\nc')) #['abc', 'a1c', 'aAc']
print(re.findall('a.c','abc a1c aAc aaaaa\nc',re.DOTALL)) #['abc', 'a1c', 'aAc', 'a\nc']
print(re.findall('a[a-z]c', 'abc a1 c aac aAcaBc asd aaaaac)) #['abc', 'aac', 'aac']
2、? :代表左边那一个字符重复0次或1次
print(re.findall('ab?','a ab abb babbb')) #['a', 'ab', 'ab', 'ab']
3、* :代表左边那一个字符出现0次或无穷次
print(re.findall('ab*','a ab abb babbb')) #['a', 'ab', 'abb', 'abbb']
4、+: 必须与其他字符连用,代表左侧的字符出现1次或者无穷次
print(re.findall('ab+', 'a ab abbb abbbb a1bbbb a-123')) #['ab', 'abbb', 'abbbb']
5、[]中 + * 不是量词,中间选一个
print(re.findall('a[-+*/]c', 'abc a1 c aAc a-c a/c a *c a+c abasd')) #['a-c', 'a/c', 'a+c']
6、^在[]内代表非
print(re.findall('a[^a-z]c', 'abc aAc aaaaac a-c a/c a *c a+c abasd')) #['aAc', 'a-c', 'a/c', 'a+c']