第六篇 常用模块

一、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=''的配置

具体应用,实现购物的认证并在日志中打印

目录结构如下:


image.png
#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 编写的匹配引擎执行。


image.png
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('<imag href="(.*)" />',
                 '<h1>hello</h1><a href="http://www.baidu.com"></a><imag href="http://www.baidu.com/a.jpg" />')) #只匹配分组内的内容

print(re.findall('<imag href="(?:.*)" />',
                 '<h1>hello</h1><a href="http://www.baidu.com"></a><imag href="http://www.baidu.com/a.jpg" />')) #前面加个?:表示把组内和组外的都匹配出来
执行结果:
['http://www.baidu.com/a.jpg']
['<imag href="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'<.*?>.*?</.*?>','<h1>hello</h1>')) #.*?表示非贪婪匹配
print(re.findall(r'<(.*?)>.*?</(.*?)>','<h1>hello</h1>'))
print(re.findall(r'<(.*?)>.*?</(\1)>','<h1>hello</h1>'))
print(re.findall(r'<(?P<k>.*?)>.*?</(?P=k)>','<h1>hello</h1>')) #表示把匹配的结果赋值给k,然后取k
执行结果:
['<h1>hello</h1>']
[('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
image.png
#时间戳转化成结构化时间
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()))
image.png
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]) #传的第二个参数
image.png
#应用实现打印进度条
前提了解内容
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命令运行
image.png

七、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 <class 'str'> #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())
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容

  • Day05的课程要点记录详细教程地址:Day5 - 常用模块学习 | 第四篇:模块 一、模块介绍 1.1 定义 模...
    乘风逐月阅读 442评论 0 1
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,579评论 18 139
  • Time 模块 时间模块常用的有如下几种。 元组形式显示时间: UTC时间: 将格式化的时间转换为元组形式的时间:...
    断尾壁虎V阅读 468评论 0 1
  • 平时获取知识和信息的渠道应该跟大家差不多,主要有以下几种: 1.互联网,主要是度娘,有什么不明白的,就搜索一下做参...
    翰墨拾光阅读 165评论 2 3
  • 这是李婷365日写作计划第262天的写作内容 邹恩润,笔名“韬奋”,意思是“韬光养晦,不懈奋斗”。他在主编《生活》...
    婷婷玉立水墨画阅读 419评论 0 0