一、logging模块
日志级别
'''
critical=50
error=40
warning=30
info=20
debug=10
notset=0
'''
# import logging 默认的日志级别是:warning,默认的输出目标是:终端
logging.debug('debug')
logging.info('info')
logging.warning('warn123')
logging.error('error')
logging.critical('critical')
#控制日志打印到文件中,并且自己定制日志的输出格式
import logging
logging.basicConfig(
filename='access.log',
# filemode='w', #默认是a模式
format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',
level=10,
)
logging.debug('debug')
logging.info('info')
logging.warning('warn123')
logging.error('error')
logging.critical('critical')
# 待解决的问题:
# 1:既往终端打印,又往文件中打印
# 2:控制输出到不同的目标(终端+文件)的日志,有各自的配置信息
import logging
# 一:Logger对象:负责产生日志信息
logger=logging.getLogger('root')
# 二:Filter对象:过滤日志,略,以后基本不用
# 三:Handler对象:负责接收Logger对象传来的日志内容,控制打印到终端or文件
h1=logging.FileHandler('t1.log') #日志输出到t1.log文件
h2=logging.FileHandler('t2.log')
h3=logging.StreamHandler() #日志输出到终端
# 四:formmater对象,定义日志格式
# 给文件
formatter1=logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',
)
#给终端
formatter2=logging.Formatter(
'%(asctime)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',
)
# 五:为handler对象绑定日志格式,设置日志级别
# 给文件:绑定到Filehandler对象
h1.setFormatter(formatter1)
h2.setFormatter(formatter1)
#给终端:绑定到Streamhandler对象
h3.setFormatter(formatter2)
# 在handler对象设置日志级别
h1.setLevel(20)
h2.setLevel(20)
h3.setLevel(20)
#六:把h1,h2,h3都add给logger,这样logger对象才能把自己的日志交给他们三负责输出
logger.addHandler(h1)
logger.addHandler(h2)
logger.addHandler(h3)
logger.setLevel(20) #logger处也设置日志级别但一定要<=Hanlder对象设置的日志级别,如果不设置默认logger设置的日志级别就是warning,logger是第一层关卡,Hanlder设置的日志级别是第二层关卡,所以第一层的日志级别要低,一般将二者设置相同即可
# 七:测试
logger.debug('debug')
logger.info('info')
logger.warning('warn123') #30
logger.error('error')
logger.critical('critical')
# Logger的继承(了解)
import logging
#定义日志格式
formatter=logging.Formatter('%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',)
#定义handler是输出到终端还是输出到文件
ch=logging.StreamHandler() #输出到终端
ch.setFormatter(formatter)
#定义logger对象,这里定义多个
logger1=logging.getLogger('root')
logger2=logging.getLogger('root.child1')
logger3=logging.getLogger('root.child1.child2')
logger1.addHandler(ch) #将logger对象和handler对象关联
logger2.addHandler(ch)
logger3.addHandler(ch)
logger1.setLevel(10)
logger2.setLevel(10)
logger3.setLevel(10)
logger1.debug('log1 debug')
logger2.debug('log2 debug')
logger3.debug('log3 debug')'''
2017-07-28 22:22:05 PM - root - DEBUG -test: log1 debug
2017-07-28 22:22:05 PM - root.child1 - DEBUG -test: log2 debug
2017-07-28 22:22:05 PM - root.child1 - DEBUG -test: log2 debug
2017-07-28 22:22:05 PM - root.child1.child2 - DEBUG -test: log3 debug
2017-07-28 22:22:05 PM - root.child1.child2 - DEBUG -test: log3 debug
2017-07-28 22:22:05 PM - root.child1.child2 - DEBUG -test: log3 debug'''
我们发现当定义多个logger对象时,当调用这些logger对象输出日志时,logger2会从logger1处继承打印2遍,logger3会从logger1和logger2处继承,打印3遍
#应用
"""
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'
# 定义日志输出格式 结束
logfile_dir = os.path.dirname(os.path.abspath(__file__)) # log文件的目录
logfile_name = 'all2.log' # log文件名
# 如果不存在定义的日志目录就创建一个
if not os.path.isdir(logfile_dir):
os.mkdir(logfile_dir)
# log文件的全路径
logfile_path = os.path.join(logfile_dir, logfile_name) #os.path.join()此函数可以将目录和文件路径拼接相当于这种形式logfile_dir/ogfile_name
# 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)传递 },
},
}
def load_my_logging_cfg():
logging.config.dictConfig(LOGGING_DIC) # 导入上面定义的logging配置
logger = logging.getLogger(__name__) # 生成一个log实例
logger.info('It works!') # 记录该文件的运行状态
if __name__ == '__main__':
load_my_logging_cfg()
logging.getLogger(__name__),不同的文件__name__不同,这保证了打印日志时标识信息不同,但是拿着该名字去loggers里找key名时却发现找不到,于是默认使用key=''的配置
具体应用,实现购物的认证并在日志中打印
目录结构如下:
#start.py
import sys,os
BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) #定义环境变量
sys.path.append(BASE_DIR) #将变量导入path路径
from core import src
if __name__ == '__main__': #如果这个文件被当做模块导入时不会执行下面的代码
src.run()
#src.py
from lib import common
#要拿到logger对象
print(__name__) #此文件被start.py文件当做模块导入,所以__name__= core.src
logger1=common.get_logger(__name__)
logger2=common.get_logger('collect')
current_user={'user':None}
def auth(func):
def wrapper(*args,**kwargs):
if current_user['user']: #刚开始为None,为假,不执行下面的代码 return func(*args,**kwargs)
return func(*args,**kwargs)
name=input('username>>: ').strip()
password=input('password>>: ').strip()
db_obj=common.conn_db() #连接数据库,拿到数据库对象
if db_obj.get(name) and password == db_obj.get(name).get('password'): ####判断用户输入的名字是否正确,如果正确再判断密码是否正确,如果正确日志就输出登陆成功
logger1.info('登录成功')
logger2.info('登录成功')
current_user['user']=name
return func(*args,**kwargs)
else:
logger1.error('登录失败')
logger2.error('登录失败')
return wrapper
@auth #shop=auth(shop)
def shop():
print('is shoping')
@auth
def run():
print('''
1 购物
2 结账
3 重置
4 退出
''')
while True:
choice=input('>>: ').strip()
if not choice:continue
if choice == '1':
shop()
#common.py
from conf import settings
import logging.config
import logging
import json
def conn_db(charset='utf-8'):
dic=json.load(open(settings.DB_PATH,encoding=charset)) #反序列化
return dic
def get_logger(name): #定义如何获取logger对象
logging.config.dictConfig(settings.LOGGING_DIC)
logger=logging.getLogger(name)
# logger=logging.getLogger('core.src')
return logger
#setting.py
import os
BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
#DB_PATH='%s%s%s%s%s' %(BASE_DIR,os.sep,'db',os.sep,'db.json')
DB_PATH=os.path.join(BASE_DIR,'db','db.json') #拼接路径,相当于BASE_DIR/db/json
LOG_PATH=os.path.join(BASE_DIR,'log','access.log')
COLLECT_PATH=os.path.join(BASE_DIR,'log','collect.log')
# 定义三种日志输出格式
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
'[%(levelname)s][%(message)s]' #其中name为getlogger指定的名字
simple_format = '[%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
id_simple_format = '%(message)s'
# 日志的配置信息
LOGGING_DIC = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': standard_format
},
'simple': {
'format': simple_format
},
'id_simple': {
'format': id_simple_format
},
},
'filters': {},
'handlers': {
#打印到终端的日志
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler', # 打印到屏幕
'formatter': 'simple'
},
#打印到文件的日志,收集info及以上的日志
'default': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,并进行日志滚动
'formatter': 'standard',
'filename': LOG_PATH, # 日志文件
'maxBytes': 1024*1024*5, # 日志大小 5M
'backupCount': 5,
'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了
},
'collect': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件
'formatter': 'id_simple',
'filename': COLLECT_PATH, # 日志文件
'maxBytes': 1024 * 1024 * 5, # 日志大小 5M
'backupCount': 5,#总共保留6个日志文件,按照大小为5M,根据时间保留最近的6个
'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了
},
},
'loggers': {
#logging.getLogger(__name__)拿到的logger配置
# l1=logging.getLogger('collect')拿到的logger配置
# l2=logging.getLogger('l1')拿到的logger配置
'': {
'handlers': ['default', 'console'], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
'level': 'DEBUG',
'propagate': True, # 向上(更高level的logger)传递
},
'collect': {
'handlers': ['collect', ], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
'level': 'DEBUG',
'propagate': False, # 向上(更高level的logger)传递,不然会logger对象继承,继承上面的,打印两遍
},
},
}
#init_sql.py
import json
#初始化数据库
dic={
'egon':{'password':'123','balance':3000},
'alex':{'password':'alex3714','balance':4000},
'ysb':{'password':'dsb','balance':5000},
}
json.dump(dic,open(r'C:\Users\Administrator\PycharmProjects\19期\day6\soft\db\db.json','w'))
#连接上数据,拿到数据对象
dic=json.load(open(r'C:\Users\Administrator\PycharmProjects\19期\day6\soft\db\db.json','r'))
print(dic.get('egon'))
print(dic.get('egon123123'))
#db.json
{"egon": {"password": "123", "balance": 3000}, "alex": {"password": "alex3714", "balance": 4000}, "ysb": {"password": "dsb", "balance": 5000}}
二、re模块
什么是正则?
正则就是用一些具有特殊含义的符号组合到一起(称为正则表达式)来描述字符或者字符串的方法。或者说:正则就是用来描述一类事物的规则。(在Python中)它内嵌在Python中,并通过 re 模块实现。正则表达式模式被编译成一系列的字节码,然后由用 C 编写的匹配引擎执行。
import re
print(re.findall('alex','12a3 alex say hello alex sb 123 _ 4%5*6'))
print(re.findall('aaa','12aaaa'))
逗号前面的是匹配规则,findall是把所有的匹配结果都打印出来
执行结果放到列表中:
['alex', 'alex']
['aaa']
print(re.findall('\w','alex say alex sb 123 _ 4%5*')) #匹配数字、字母及下划线
print(re.findall('\W','alex say alex sb 123 _ 4%5*')) #匹配非数字、字母及下划线
print(re.findall('\s','al\te\nx hello alex sb 123 _ 4%5*')) #匹配空字符,包括\t、\n
print(re.findall('\S','al\te\nx hello alex sb 123 _ 4%5*')) #匹配非空字符
print(re.findall('\d','al\te\nx hello alex sb 123 _ 4%5*')) #匹配一个数字
print(re.findall('\d\d','al\te\nx hel12345lo alex sb 123 _ 4%5*')) #匹配两个连续数字
print(re.findall('\D','al\te\nx hello alex sb 123 _ 4%5*')) #匹配任意非数字
print(re.findall('\Al','alex say hello')) #匹配以什么开头
print(re.findall('llo\Z','alex say hello')) #匹配以什么结尾
等同如下
print(re.findall('^l','alex say hello'))
print(re.findall('llo$','alex say hello'))
print(re.findall('\n','al\te\nx hello alex sb 123 _ 4%5*')) #匹配换行符
print(re.findall('\t','al\te\nx hello alex sb 123 _ 4%5*')) #匹配制表符
#重复匹配:. [] ? * + {}
print(re.findall('a.c','a1c a%c abc accc acccc')) #匹配任意一个字符
print(re.findall('a.c','a1c a%c a\nc accc acccc',re.S)) #要想匹配换行符,需要加re.S
print(re.findall('a.c','a1c a%c a\nc accc acccc',re.DOTALL)) #或者写成这种格式
print(re.findall('a[0-9]c','a1c a%c a\nc accc acccc',re.S))
print(re.findall('a[a-z]c','a1c a%c a\nc accc acccc',re.S))
print(re.findall('a[A-Z]c','a1c a%c a\nc accc acccc aAc aAAc',re.S))
print(re.findall('a[0-9a-zA-Z]c','a1c a%c a\nc accc acccc aAc aAAc',re.S)) #匹配数字和字母
print(re.findall('a[% ]c','a c a1c a%c a+c a-c a/c a*c',re.S)) #代表a和c中间是%或者空格
print(re.findall('a[^% ]c','a c a1c a%c a+c a-c a/c a*c',re.S)) #代表取反,不是%或者空格的
print(re.findall('a[+\-*/]c','a c a1c a%c a+c a-c a/c a*c',re.S)) #-还表示从哪到哪,如果减号在中间需要转义
print(re.findall('a[-+*/]c','a c a1c a%c a+c a-c a/c a*c',re.S)) #也可以放到第一个,就不会代表从哪到哪
print(re.findall('a[+*/-]c','a c a1c a%c a+c a-c a/c a*c',re.S))
#?:左边那个字符出现0次或1次,表示b可有可无
print(re.findall('ab?','a ab abb abbb abbbbbb'))
print(re.findall('ab{0,1}','a ab abb abbb abbbbbb'))
#*:左边那个字符出现0次或无穷次
print(re.findall('ab*','a ab abb abbb abbbbbb abbc123bbbb'))
print(re.findall('ab{0,}','a ab abb abbb abbbbbb abbc123bbbb'))
#+:左边那个字符出现1次或无穷次
print(re.findall('ab+','a ab abb abbb abbbbbb abbc123bbbb'))
print(re.findall('ab{1,}','a ab abb abbb abbbbbb abbc123bbbb'))
#{n,m}:左边那个字符出现n到m次
print(re.findall('ab{3}','a ab abb abbb abbbbbb abbc123bbbb'))
print(re.findall('ab{3,}','a ab abb abbb abbbbbb abbc123bbbb'))
print(re.findall('ab{0,1}','a ab abb abbb abbbbbb abbc123bbbb'))
print(re.findall('ab{0,}','a ab abb abbb abbbbbb abbc123bbbb'))
print(re.findall('ab{1,}','a ab abb abbb abbbbbb abbc123bbbb'))
#贪婪匹配:.*
print(re.findall('a.*b','a123b456b'))
#非贪婪匹配:.*?,这里的?不是可有可无的意思
print(re.findall('a.*?b','a123b456b'))
# 分组:()
print(re.findall(' ',
'hello
')) #只匹配分组内的内容
print(re.findall(' ',
'hello
')) #前面加个?:表示把组内和组外的都匹配出来
执行结果:
['http://www.baidu.com/a.jpg']
[' ']
#| 或
print(re.findall('compan(?:ies|y)','Too many companies have gone bankrupt, and the next one is my company'))
执行结果:
['companies', 'company']
print(re.findall('a\\\\c','a\c a12 a2c')) #'a\\c',先是交给python解释器,一个\去转义,然后交给re模块,还需要一个\去转义
print(re.findall(r'a\\c','a\c a12 a2c')) #'a\\c' #可以在前面加个r表示不用交给python解释器,保持原生态交给re模块
#search
#与findall用法完全一致,不一样的地方在于search匹配一次就结束
print(re.search('alex','alex say hello alex').group())
#match代表匹配以什么开头
print(re.match('alex','alex say hello alex').group())
print(re.search('^alex','alex say hello alex').group())
# re.split() 表示以什么为分隔符切割
print(re.split(':','root:x:0:0:/root::/bin/bash'))
# re.sub()
print(re.sub('alex','SB','alex say i have on telsa my name is alex',1))
print(re.sub('alex','SB','alex say i have on telsa my name is alex')) #不写替换几次表示所有都被替换
print(re.subn('alex','SB','alex say i have on telsa my name is alex')) #subn还打印替换的次数
执行结果
SB say i have on telsa my name is alex
SB say i have on telsa my name is SB
('SB say i have on telsa my name is SB', 2)
print(re.sub(r'(\w+)(\W+)(\w+)(\W+)(\w+)',r'\5\2\3\4\1','alex-love: SB')) #前面加个r表示源生态,这样不用多家转义符了
print(re.sub(r'^al',r'AAAAAAAAA','alex-love: SB alex'))
执行结果:
SB-love: alex
AAAAAAAAAex-love: SB alex
# re.compile() 正则表达式的重用
obj=re.compile(r'^alex')
print(obj.findall('alex say hello alex'))
print(obj.search('alex say hello alex').group())
#补充:
print(re.findall(r'<.*?>.*?','hello
')) #.*?表示非贪婪匹配
print(re.findall(r'<(.*?)>.*?(.*?)>','hello
'))
print(re.findall(r'<(.*?)>.*?(\1)>','hello
'))
print(re.findall(r'<(?P.*?)>.*?(?P=k)>','hello
')) #表示把匹配的结果赋值给k,然后取k
执行结果:
['hello
']
[('h1', 'h1')]
[('h1', 'h1')]
['h1']
print(re.findall('\-?\d+\.\d+|(\-?\d+)',"1-12*(60+(-40.35/5)-(-4*3))")) #只匹配整数,用|符合表示匹配|左边的不成立才匹配右面的
print(re.findall('\-?\d+\.?\d*',"1-12*(60+(-40.35/5)-(-4*3))")) #匹配所有的数字
print(re.findall('\-?\d+\.\d+',"1-12*(60+(-40.35/5)-(-4*3))")) #匹配所有的小数
expression='1-2*((60+2*(-3-40.0/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))'
print(re.search(r'\(([-+*/]?\d+\.?\d*)+\)',expression).group()) #先取出最里面的小括号里面的内容,然后计算出结果,把这个结果填到上面表达式处,在取最里面的小括号计算出结果,这样上面的表达式就会越算越短
print(eval(expression)) #可以用此方法核实最后的计算结果
三、time与datetime模块
import time
#时间戳
print(time.time()) #距离1970年1月1日0时0分0秒有多少秒
#结构化的时间
print(time.localtime())
print(time.localtime().tm_year)
print(time.gmtime()) #格林尼治时间
执行结果:
time.struct_time(tm_year=2018, tm_mon=5, tm_mday=22, tm_hour=16, tm_min=31, tm_sec=25, tm_wday=1, tm_yday=142, tm_isdst=0)
2018
time.struct_time(tm_year=2018, tm_mon=5, tm_mday=22, tm_hour=8, tm_min=31, tm_sec=25, tm_wday=1, tm_yday=142, tm_isdst=0)
#格式化的字符串
print(time.strftime('%Y-%m-%d %H:%M:%S'))
print(time.strftime('%Y-%m-%d %X'))
2018-05-22 16:31:25
2018-05-22 16:31:25
#时间戳转化成结构化时间
print(time.localtime(123123123))
print(time.gmtime(123123123))
#结构化时间转化成时间戳
print(time.mktime(time.localtime()))
#格式化的字符串转化成结构化时间
print(time.strptime('2017:03-01','%Y:%m-%d'))
#结构化时间转化成格式化的字符串
print(time.strftime('%Y-%m-%d %X',time.gmtime()))
print(time.asctime(time.localtime()))
print(time.ctime(time.time()))
Tue May 22 17:24:19 2018
Tue May 22 17:24:19 2018
#import datetime模块
print(datetime.datetime.now())
print(datetime.datetime.now()+datetime.timedelta(days=3)) #三天后时间
print(datetime.datetime.now()-datetime.timedelta(days=3)) #三天前时间
print(datetime.datetime.now()+datetime.timedelta(days=-3)) #三天前
print(datetime.datetime.now()+datetime.timedelta(hours=3))#三个小时后
print(datetime.datetime.now()+datetime.timedelta(minutes=3))#三分钟后
print(datetime.datetime.fromtimestamp(123123123)) #显示时间戳
print(datetime.datetime.now().replace(hour=22)) #将小时替换为22
2018-05-22 17:17:12.227223
2018-05-25 17:17:12.227223
2018-05-19 17:17:12.227223
2018-05-19 17:17:12.227223
2018-05-22 20:17:12.227223
2018-05-22 17:20:12.227223
1973-11-26 08:52:03
2018-05-22 22:23:11.135378
四、random模块
import random
print(random.random())#(0,1)----float 大于0且小于1之间的小数
print(random.randint(1,3)) #[1,3] 大于等于1且小于等于3之间的整数
print(random.randrange(1,3)) #[1,3) 大于等于1且小于3之间的整数
print(random.choice([1,'23',[4,5]]))#列表中的元素随机取一个
print(random.sample([1,'23',[4,5]],2))#列表元素随机取2个
print(random.uniform(1,3))#大于1小于3的小数
item=[1,3,5,7,9]
random.shuffle(item) #打乱item的顺序,相当于"洗牌"
print(item)
举例:打印随机验证码
def make_code(n):
res=''
for i in range(n):
s1=str(random.randint(0,9))
s2=chr(random.randint(65,90)) #chr将数字转化成ASIIC表中对应的字符,65到90是大写字母
res+=random.choice([s1,s2]) #字符串拼接
return res
print(make_code(10))
五、OS模块
os模块是与操作系统交互的一个接口
os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径
os.chdir("dirname") 改变当前脚本工作目录;相当于shell下cd
os.curdir 返回当前目录: ('.')
os.pardir 获取当前目录的父目录字符串名:('..')
os.makedirs('dirname1/dirname2') 可生成多层递归目录
os.removedirs('dirname1') 若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.mkdir('dirname') 生成单级目录;相当于shell中mkdir dirname
os.rmdir('dirname') 删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname
os.listdir('dirname') 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
os.remove() 删除一个文件
os.rename("oldname","newname") 重命名文件/目录
os.stat('path/filename') 获取文件/目录信息
os.sep 输出操作系统特定的路径分隔符,win下为"\",Linux下为"/"
os.linesep 输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n"
os.pathsep 输出用于分割文件路径的字符串 win下为;,Linux下为:
os.name 输出字符串指示当前使用平台。win->'nt'; Linux->'posix'
os.system("bash command") 运行shell命令,直接显示
os.environ 获取系统环境变量
os.path.abspath(path) 返回path规范化的绝对路径
os.path.split(path) 将path分割成目录和文件名二元组返回
os.path.dirname(path) 返回path的目录。其实就是os.path.split(path)的第一个元素
os.path.basename(path) 返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素
os.path.exists(path) 如果path存在,返回True;如果path不存在,返回False
os.path.isabs(path) 如果path是绝对路径,返回True
os.path.isfile(path) 如果path是一个存在的文件,返回True。否则返回False
os.path.isdir(path) 如果path是一个存在的目录,则返回True。否则返回False
os.path.join(path1[, path2[, ...]]) 将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
os.path.getatime(path) 返回path所指向的文件或者目录的最后存取时间
os.path.getmtime(path) 返回path所指向的文件或者目录的最后修改时间
os.path.getsize(path) 返回path的大小
import os
res=os.system('tasklist') #os.system()会执行系统命令并在终端打印,tasklist相当于linux中的ps
print('==========================?>',res) #发现打印的是返回值0或者1
print(os.path.split(r'a\b\c\d.txt') ) #切分目录名和文件名
print(os.path.dirname(r'a\b\c\d.txt') ) #取目录名
print(os.path.basename(r'a\b\c\d.txt') ) #取文件名
('a\\b\\c', 'd.txt')
a\b\c
d.txt
#以下两种方法都可以取文件的大小
print(os.stat(r'D:\张大志迈远\pythontest\day6\soft\conf\settings.py').st_size)
print(os.path.getsize(r'D:\张大志迈远\pythontest\day6\soft\conf\settings.py'))
#在windows中将/转化为\,将大写字母转为小写字母,linux中不适用
print(os.path.normcase('c:/Windows\\system32\\'))
c:\windows\system32\
#把..转化成上一级目录,把分隔符都转化成右斜杠,可以用来规范化路径
print(os.path.normpath('c://windows\\System32\\../Temp/') )
c:\windows\Temp
#以下两种方式都可以表示__file__文件的上一级的上级目录
x=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
print(x)
print(os.path.normpath(os.path.join(
os.path.abspath(__file__),
'..',
'..'
)
)) #此种方法在openstack的源码中使用,建议使用第一种方式
六、sys模块
#sys模块用于导入环境变量等
1 sys.argv 命令行参数列表,第一个元素是程序本身路径
2 sys.exit(n) 退出程序,正常退出时exit(0)
3 sys.version 获取Python解释程序的版本信息
4 sys.maxint 最大的Int值
5 sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
6 sys.platform 返回操作系统平台名称
import sys
print(sys.argv)
print(sys.argv[0]) #第一个元素为程序本身的路径
print(sys.argv[1]) #第二个元素为执行python脚本时传的一个参数
print(sys.argv[2]) #传的第二个参数
#应用实现打印进度条
前提了解内容
print('[%-10s]' %'#') #打印#号并指定宽度
print('[%-10s]' %'##')
print('[%-10s]' %'###')
print('[%-10s]' %'####')
print('[%-10s]' %'#####')
print('%d%%' %30) #%可以对%进行转义,两个%的含义不再是定义数据类型,而是真正的百分号的含义
print('[%%-%ds]' %50)
print(('[%%-%ds]' %50) %'#')
[# ]
[## ]
[### ]
[#### ]
[##### ]
30%
[%-50s]
[# ]
import sys
import time
def progress(percent,width=50): #width是定义进度条的宽度,也就是有多少个#
if percent > 1:
percent=1
show_str=('[%%-%ds]' %width) %(int(percent*width)*'#') #当进度为100%时打印50个#
print('\r%s %s%%' %(show_str,int(percent*100)),end='',file=sys.stdout,flush=True) #\r表示不换行光标移动到行首,print括号里前面是定义格式,后面是定义打印的内容,%s表示字符串格式,用后面的打印内容替换%s,sys.stdout表示输出到终端,flush=True表示每次循环完就打印一次,避免形成堆积,一次全打印出来
total_size=10212
recv_size=0
while recv_size < total_size:
time.sleep(0.2) #1024
recv_size+=1024
percent=recv_size/total_size
progress(percent,width=50)
注意上面的代码在pycharm里无法运行。要到cmd里使用python命令运行
七、shutil模块
高级的文件、文件夹、压缩包 处理模块
将文件内容拷贝到另一个文件中
import shutil
shutil.copyfileobj(open('old.xml','r'), open('new.xml', 'w'))
拷贝文件
shutil.copyfile('f1.log', 'f2.log') #目标文件无需存在
仅拷贝权限。内容、组、用户均不变
shutil.copymode('f1.log', 'f2.log') #目标文件必须存在
仅拷贝状态的信息,包括:mode bits, atime, mtime, flags
shutil.copystat('f1.log', 'f2.log') #目标文件必须存在
拷贝文件和权限
shutil.copy('f1.log', 'f2.log')
拷贝文件和状态信息
shutil.copy2('f1.log', 'f2.log')
递归的去拷贝文件夹
shutil.copytree('folder1', 'folder2', ignore=shutil.ignore_patterns('.pyc', 'tmp')) #目标目录不能存在,注意对folder2目录父级目录要有可写权限,ignore的意思是排除
递归的去删除文件
shutil.rmtree('folder1')
递归的去移动文件,它类似mv命令,其实就是重命名。
shutil.move('folder1', 'folder3')
shutil.make_archive(base_name, format,...)
创建压缩包并返回文件路径,例如:zip、tar
创建压缩包并返回文件路径,例如:zip、tar
base_name: 压缩包的文件名,也可以是压缩包的路径。只是文件名时,则保存至当前目录,否则保存至指定路径,
如 data_bak =>保存至当前路径
如:/tmp/data_bak =>保存至/tmp/
format: 压缩包种类,“zip”, “tar”, “bztar”,“gztar”
root_dir: 要压缩的文件夹路径(默认当前目录)
owner: 用户,默认当前用户
group: 组,默认当前组
logger: 用于记录日志,通常是logging.Logger对象
举例
#打包压缩:day5_bak.tar.gz
import shutil
shutil.make_archive('day5_bak','gztar',root_dir=r'D:\张大志迈远\pythontest\day5')
解包
import tarfile
obj=tarfile.open('day5_bak.tar.gz')
obj.extractall('aaa') #解包到哪个目录下
obj.close()
shutil 对压缩包的处理是调用 ZipFile 和 TarFile 两个模块来进行的,详细:
import zipfile
# 压缩
import zipfile
z=zipfile.ZipFile('nihao.zip', 'w')
z.write('t1.log') #将t1.log文件压缩为nihao.zip
z.close()
# 解压
z = zipfile.ZipFile('nihao.zip', 'r')
z.extractall(path='aaa') #将nihao.zip文件解压到aaa目录
z.close()
#打包
import tarfile
t=tarfile.open('m.tar','w')
t.add('access.log') #将此文件压缩为m.tar
t.close()
#解包
t=tarfile.open('m.tar','r')
t.extractall('aaa')
t.close()
八、json&pickle模块
之前我们学习过用eval内置方法可以将一个字符串转成python对象,不过,eval方法是有局限性的,对于普通的数据类型,json.loads和eval都能用,但遇到特殊类型的时候,eval就不管用了,所以eval的重点还是通常用来执行一个字符串表达式,并返回表达式的值。
import,json2
x="[null,true,false,1]"
print(eval(x)) #报错,无法解析null类型,而json就可以
print(json.loads(x))
为什么要序列化?
1:持久保存状态
需知一个软件/程序的执行就在处理一系列状态的变化,在编程语言中,'状态'会以各种各样有结构的数据类型(也可简单的理解为变量)的形式被保存在内存中。
内存是无法永久保存数据的,当程序运行了一段时间,我们断电或者重启程序,内存中关于这个程序的之前一段时间的数据(有结构)都被清空了。
在断电或重启程序之前将程序当前内存中所有的数据都保存下来(保存到文件中),以便于下次程序执行能够从文件中载入之前的数据,然后继续执行,这就是序列化。
具体的来说,你玩使命召唤闯到了第13关,你保存游戏状态,关机走人,下次再玩,还能从上次的位置开始继续闯关。或如,虚拟机状态的挂起等。
2:跨平台数据交互
序列化之后,不仅可以把序列化后的内容写入磁盘,还可以通过网络传输到别的机器上,如果收发的双方约定好实用一种序列化的格式,那么便打破了平台/语言差异化带来的限制,实现了跨平台数据交互。
反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。
如何序列化之json和pickle:
json
如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便。
Pickle可以支持所有的python数据类型,但不能跨平台
import json
d={'a':1}
# print(type(json.dumps(d))) #发现序列化之后是json格式的字符串类型
with open('1.json','w') as f: #以文本格式打开一个文件
f.write(json.dumps(d)) #并且可以往文件里写json格式的字符串类型
with open('2.json','r') as f: #写一个2.json的json格式文件
data=f.read()
print(json.loads(data)['a']) #反序列化之后取出键为a的值
import json
d={'a':1}
s_json=json.dumps(d) #序列化
print(s_json,type(s_json))
print(json.loads(s_json)['a']) #反序列化,json.loads()传的参数是json格式的字符串才可以
print(json.loads('{"name":1}')['name']) #只要loads传的参数是json格式的字符串就可以反解它,不一定非得用json.dump序列化
l=[1,2,3,'a']
print(json.loads('[1,2,3,"a"]')[1])
#json.dump和json.load的用法
import json,pickle
d={'a':1}
json.dump(d,open('3.json','w')) #json.dump的用法,将d这个字典格式化为json格式后写到文件中
print(json.load(open('3.json','r'))['a']) #从文件中反序列化并取值
#pickle的用法
import pickle
d={'a':1}
d_pkl=pickle.dumps(d)
print(d_pkl,type(d_pkl)) #发现序列化后是bytes类型
with open('1.pkl','wb') as f:
f.write(d_pkl) #将bytes类型写到1.pkl文件中
可以简写为如下方式
import pickle
d={'a':1}
pickle.dump(d,open('2.pkl','wb')) #将序列化的结果写到2.pkl文件中
x=pickle.load(open('2.pkl','rb')) #反序列化为字典格式
print(x,type(x))
import json,pickle
def func():
print('from 序列表.py')
# json.dumps(func) #发现不支持函数的数据类型
print(pickle.dumps(func)) #但pickle支持所有的数据类型,函数也可以序列化
pickle.dump(func,open('3.pkl','wb')) #将函数序列化后写到文件中
以后通常都用json进行序列化
九、shelve模块
shelve模块比pickle模块简单,只有一个open函数,返回类似字典的对象,可读可写;key必须为字符串,而值可以是python所支持的数据类型
import shelve
dic={'a':1,'b':2}
d=shelve.open(r'db.shl') #打开一个文件
d['x']=dic #将字典序列化后保存到db.shl文件中
d.close()
obj=shelve.open(r'db.shl')
print(obj['x']['a']) #反序列化这个文件
obj.close()
总结shelve模块可以更简单的序列化和反序列化字典对象
十、configparser模块
用来解析配置文件内容的模块
#my.cnf
[mysql]
user = root
password = 123
[mysqld]
port = 3306
x=true
y=1.3
import configparser
obj=configparser.ConfigParser()
obj.read('my.cnf') #读取上面的文件
print(obj.sections()) #取标题
print(obj.options('mysql')) #取标题下面的key
print(obj.items('mysql')) #取标题下面的key和value并放在元组中
print(obj.get('mysql','user')) #get方法先传标题,再传key,
root #可以获取value
x=obj.get('mysqld','port')
print(x,type(x))
3306 #get方法拿到的结果是字符串类型
print(type(obj.getint('mysqld','port'))) #可以取完之后自动转成整形
print(type(obj.getboolean('mysqld','x'))) #取完之后转成波尔形
print(type(obj.getfloat('mysqld','y'))) #转成浮点型
#判断是否存在
import configparser
obj=configparser.ConfigParser()
obj.read('my.cnf')
print(obj.has_section('mysql')) #判断是否有这个标题
print(obj.has_option('alex','is_sbxxxxxxxxxxx')) #判断是否有标题下的key
#了解:修改操作
import configparser
obj=configparser.ConfigParser()
obj.read('my.cnf')
obj.add_section('alex') #添加一个标题
obj.set('alex','password','123') #在标题下添加内容,注意添加的键和值都要是字符串类型
obj.set('alex','is_sb','True')
obj.write(open('my.cnf','w')) #必须要写到文件里才能对文件进行修改
import configparser
obj=configparser.ConfigParser()
obj.read('my.cnf')
obj.remove_section('mysqld') #删除标题
obj.write(open('my.cnf','w'))
obj.remove_option('mysql','user') #删除标题下的键
十一、hashlib模块
1、什么叫hash:hash是一种算法(3.x里代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法),该算法接受传入的内容,经过运算得到一串hash值
2、hash值的特点是:
2.1 只要传入的内容一样,得到的hash值必然一样
2.2 不能由hash值返解成内容,不可被逆推
2.3 只要使用的hash算法不变,无论校验的内容有多大,得到的hash值长度是固定的
import hashlib
m=hashlib.md5() #指明算法为md5
m.update('hello'.encode('utf-8')) #要转化成bytes类型才能hash运算
m.update('world'.encode('utf-8'))
print(m.hexdigest()) #上面两个字符串的hash结果为fc5e038d38a57032085441e7fe7010b0
m=hashlib.md5()
m.update('helloworld'.encode('utf-8'))
print(m.hexdigest())
发现两次的hash结果是一样的fc5e038d38a57032085441e7fe7010b0
说明update两次相当于对两个字符串合起来取hash值
with open(r'D:\张大志迈远\pythontest\day6\7_sys模块.py','rb') as f:
m=hashlib.md5()
m.update(f.read())
print(m.hexdigest()) #1367a8c7a2953ad715b6b74e3838074e
相当于把文件的的内容一次性的读出来让后取hash值
with open(r'D:\张大志迈远\pythontest\day6\7_sys模块.py','rb') as f:
m=hashlib.md5()
for line in f:
m.update(line) #一行一行的update取hash值
print(m.hexdigest()) #1367a8c7a2953ad715b6b74e3838074e
一般用这种方式取hash值,因为如果文件太大的话会很慢
s='alex3714'
m=hashlib.md5()
m.update(s.encode('utf-8'))
s_hash=m.hexdigest()
print(s_hash)
#密码破解
前提准备
dic={}
dic[0]=1
dic[1]=2
print(dic)
{0: 1, 1: 2}
实例
passwds=[
'alex3714',
'123456',
'alex123',
'123alex',
'Alex@3012'
]
def make_dic(passwds):
dic={} #dic是一个字典
for passwd in passwds:
m=hashlib.md5()
m.update(passwd.encode('utf-8'))
dic[passwd]=m.hexdigest()
return dic
# print(make_dic(passwds)) #打印函数的返回值发现是一个字典
{'alex3714': 'aee949757a2e698417463d47acac93df', '123456': 'e10adc3949ba59abbe56e057f20f883e', 'alex123': 'b75bd008d5fecb1f50cf026532e8ae67', '123alex': '2aca953388ccf2924d3ff595c31168d0', 'Alex@3012': '19410e280bd32a66b27045fd430f5190'}
def break_code(s1,dic):
for p in dic: #p会遍历字典的每一个key
if s1 == dic[p]:
return p
s1='aee949757a2e698417463d47acac93df'
dic=make_dic(passwds)
res=break_code(s1,dic) #res等于这个函数的返回值,也就是p
print(res)
打印结果为alex3714,发现密码被破解
#密码加盐
import hashlib
m=hashlib.md5('天王盖地虎'.encode('utf-8')) #在hash算法时加盐,也就是在密码开头加盐
# m=hashlib.sha512('天王盖地虎'.encode('utf-8'))
m.update('alex3714'.encode('utf-8'))
m.update('宝塔镇河妖'.encode('utf-8')) #在密码的后面加盐
print(m.hexdigest()) #b74c5a073f1faf83dbc7b3c30a10ef4d
import hmac
#要想保证俩次校验的结果是一样的,除了内容必须一致以外,key必须一样
m1=hmac.new('哈了个哈'.encode('utf-8')) #跟hashlib模块不一样,必须传参,不然会报错
m1.update('alex3714'.encode('utf-8'))
print(m1.hexdigest())
m2 = hmac.new('哈'.encode('utf-8'))
m2.update('了个哈alex3714'.encode('utf-8'))
print(m2.hexdigest())
发现两次hash值不一样了
#只有保证key一样,也就是 hmac.new后的参数一样,hash值才能一样
m3 = hmac.new('哈了个哈'.encode('utf-8'))
m3.update('alex'.encode('utf-8'))
m3.update('3714'.encode('utf-8'))
print(m3.hexdigest())