Python常用模块


阅读目录

一、optparse

二、configparser

三、logging

四、zipfile

一、optparse

1 模块介绍

optparse是一个比旧的getopt模块更方便、灵活和强大的解析命令行选项的库

2 简单示例

编辑test.py

from optparse import OptionParser

# 实例对象
parser = OptionParser()  
# 添加参数 -f 指定一个文件名称
parser.add_option("-f", "--file", dest="filename",
                  help="write report to FILE", metavar="FILE")
parser.add_option("-q", "--quiet",
                  action="store_false", dest="verbose", default=True,
                  help="don't print status messages to stdout")
# 解析参数
(options, args) = parser.parse_args()
print("options:", options)  # {'filename': 'aaa', 'verbose': False}
print("args:", args)        # ['bbb']

# 终端输入:python test.py -f aaa -q bbb 

3 参数介绍

 action     解析选项时执行的动作:
            'store'——选项有一个参数需要读取和保存,如果没有任何显示指定动作,这就是默认动作
            'store_const'——选项不带任何参数,但是当遇到选项时,就会保存const关键字参数指定的常量值
            'stone_true'——解析选项时,保存的是BOOL值
            'store_false'——解析选项时,保存的是BOOL值
            'append'——选项有一个参数,解析时被附加到一个列表
            'count'——选项不带任何参数,但是保存一个计数器,遇到参数,计数器的值就会增加
            'callback'——遇到选项时,调用callback关键字指定的一个回调函数
            'help'——解析选项时打印一条帮助消息
            'version'——解析选项是获取指定文件版本
 callback   指定遇到选项时候调用的回调函数 callback(option,opt_str,value,parse,*rags,**kwarg)
 choices    指定所有可能的选项值的字符串列表,当一个选项只有一组有限的值时候使用
 const      通过store_const动作保存的常量值
 default    默认值None
 dest       设置用于保存解析期间选项值的属性名称
 help       这个特定选项的帮助文档
 metavar    指定打印帮助文本时使用的选项参数的名称
 nargs      为需要参数的动作指定选项参数的数量
 type       指定选项的类型

4 parser对象常用方法

parser.disable_interspersed_args()   #不接受简单选项和位置参数的混合使用
parser.enable_interspersed_args()    #选项与位置参数可以混合使用
parser.parse_args()                  #解析命令行选项,并返回一个元组(options,args)options包含所有选项的值得对象,args是所有余下位置参数的列表
parser.set_defaults()                #设置特定选项目的的默认值

5 实战应用

class SysEntrance(object):
    """系统入口"""

    def __init__(self):
        # 创建OptionParser类对象
        self.parser = optparse.OptionParser()
        self.add_option()
        self.options, self.args = self.parser.parse_args()  # 获取格式化获取的信息

    def add_option(self):
        """
        添加选项
        """
        self.parser.add_option("-d", "--dp", dest="path", metavar="PROFILE")  # 目录
        self.parser.add_option("-t", "--et", dest="type", choices=['1', '2', '3'])  # 执行类型
        self.parser.add_option("-b", "--build", action="callback", callback=self.vararg_callback, dest="build",
                               help="build", metavar="PROFILE")
        self.parser.add_option("-g", "--debug", action="store_true", dest="debug",
                               help="build with debug symbols, affects only full build")

    def vararg_callback(self, option, opt_str, value, parser):
        """
        调用parser.parse_args()触发回调函数, 可以实现对选项参数值的加工

        :param option:  optparse.Option类
        :param opt_str: 选项参数 -b/--build
        :param value:   //可以留言告诉我是什么值
        :param parser: optparse.OptionParser对象
        :return:
        """
        assert value is None
        value = []
        for arg in parser.rargs:
            # stop on --foo like options
            if arg[:2] == "--" and len(arg) > 2:
                break
            # stop on -a, but not on -3 or -3.0
            if arg[:1] == "-" and len(arg) > 1:
                break
            value.append(arg)

        del parser.rargs[:len(value)]
        # 重新设置dest对应的值
        setattr(parser.values, option.dest, value)

    def check_option(self):
        """检查option选项"""
        if self.options.path and not os.path.exists(self.options.__dict__.get('path')):
            return "选项[path]指定错误 >> 请指定有效的文件路径"

    def run(self):
        """根据配置信息进入不同的业务层"""
        err = self.check_option()
        if err:
            return None, err
        # 业务层
        if self.options.debug:
            global DEBUG
            DEBUG = True
        if self.options.type:
            return func(), None


DEBUG = False


def func():
    global DEBUG
    if DEBUG:
        print(1111)
    else:
        print(2222)


if __name__ == "__main__":
    _, err = SysEntrance().run()
    print(err)

二、configparser

1 模块介绍

configparser库实现了一种基本的配置文件解析语言,它提供了一种类似于microsoft windows ini文件的结构。

点击查看 - configparser官网

2 简单示例

import configparser
parser = configparser.ConfigParser()
parser.read_dict({'section1': {'key1': 'value1',
                               'key2': 'value2',
                               'key3': 'value3'},
                  'section2': {'keyA': 'valueA',
                               'keyB': 'valueB',
                               'keyC': 'valueC'},
                  'section3': {'foo': 'x',
                               'bar': 'y',
                               'baz': 'z'}
                  })
print(parser.sections()) 
# ['section1', 'section2', 'section3']
print([option for option in parser['section3']])
# ['foo', 'bar', 'baz']

3 常用介绍

3.1 读取配置文件

配置文件pro.ini

# 注释1
; 注释2

# DEFAULT为其它sections提供默认值
[DEFAULT]
a = 45

[Common]
# 引用同一节点
home_dir: /Users
my_dir: %(home_dir)s/lumberjack
my_pictures: %(my_dir)s/Pictures

[section1]
k1 = v1
k2:v2
# 引用不同节点 -> python3.8
path: ${Common:my_dir}/Library/Frameworks/

[section2]
k1 = v1
# 空值
k2 = 
k3 = true
k4 = 22.05

Note1:在配置文件中%是唯一需要转义的字符,10%可使用10%%代替
Note2:key值不区分大小写,都会被转化为小写

import configparser

config=configparser.ConfigParser()
config.read('pro.ini')

#查看所有的标题
res=config.sections() 
print(res) # ['Common', 'section1', 'section2']

#查看标题section1下所有key=value的key
options=config.options('section1')
print(options) #['k1', 'k2', 'path', 'a']

#查看标题section2下所有key=value的(key,value)格式
item_list=config.items('section2')
print(item_list) #[('a', '45'), ('k1', 'v1'), ('k2', '')]

#查看标题section1下k1的值=>字符串格式
val=config.get('section1','k1')
print(val) #v1

#查看标题section1下a的值=>整数格式
val1=config.getint('section1','a')
print(val1) #45

#查看标题section2下k3的值=>布尔值格式
val2=config.getboolean('section2','k3')
print(val2) #True

#查看标题section2下k4的值=>浮点型格式
val3=config.getfloat('section2','k4')
print(val3) #22.05

#删除整个标题section2
config.remove_section('section2')

#删除标题section1下的某个k1和k2
config.remove_option('section1','k1')
config.remove_option('section1','k2')

#判断标题section1下是否有k1
print(config.has_option('section1','k1'))

#判断是否存在某个标题
print(config.has_section('section1'))

#添加一个标题
config.add_section('Paths')

#在标题Paths下添加p1=/root,p2=/tmp的配置, 注意value值必须为字符串
config.set('Paths','p1','/root')
config.set('Paths','p2','/tmp') 

#最后将修改的内容写入文件,完成最终的修改
config.write(open('pro.ini','w'))

4 实战应用

4.1 key值区分大小写

from configparser import ConfigParser

class NewConfigParser(ConfigParser):
    def __init__(self, defaults=None):
        ConfigParser.__init__(self, defaults=defaults)

    """复写方法实现key值区分大小写"""
    def optionxform(self, optionstr):
        return optionstr

4.2 实现配置文件启动系统

借助上面optparse的知识,实现一个可以通过命令行调用配置文件启动系统的功能

class ExecuteConfig(File):
    """执行器配置文件"""
    def __init__(self):
        self.configParser = ReConfigParser()

    def analysis(self, filename):
        """解析配置文件"""
        filepath = self.join_path(self.base_dir, filename)
        if not self.is_exist(filepath):
            return None, "配置文件异常 >> 配置文件不存在"

        self.configParser.read(filepath, encoding='utf-8')

        items = self.configParser.items('execute')   # 规定解析头必须为execute

        return items, None

class Options(object):

    def __init__(self):
        self.parser = OptionParser()
        self.add_option()

        self.options_dict = dict()  # 保存选项解析结果

        self.options, self.args = self.parser.parse_args()  # 获取格式化获取的信息

        self.pretreatment()

    def add_option(self):
        """
        添加选项
        :return:
        """
        self.parser.add_option("--ini", dest="ini")  # 启动配置
        '''
        自定制命令
        '''

    def pretreatment(self):
        """预处理"""
        if self.options.ini:
            # 启动文件
            config = ExecuteConfig()
            items, err = config.analysis(self.options.ini)
            if err:
                raise ValueError(err)
            for item in items:
                if hasattr(self, f'handle_{item[0]}'):
                    err = getattr(self, f'handle_{item[0]}')(item[1])
                    if err:
                        raise ValueError(err)
                else:
                    raise ValueError(f"选项解析失败 >> 无效配置参数【{item[0]}】")

    def handle_ini(self, args):
        pass
    
    '''处理选项操作逻辑'''
    def handle_logType(self, args):
        if not args:
            reutrn "选项错误 >> logType不能为空"
        self.options_dict.update({"logType": args})
        

    def handle_logLevel(self, args):
        if args:
            self.options_dict.update({"logLevel": args})

    def handle_logTo(self, args):
        if args:
            self.options_dict.update({"logTo": args})

接下来在系统入口实例化Options()类就可以实现配置文件启动了

三、logging

1 模块介绍

logging库提供python程序日志记录功能

点击查看 - logging官网

2 简单示例

import logging

logging.debug("debug 日志")
logging.info("info 日志")
logging.warning("warning 日志")
logging.error("error 日志")
logging.critical("critical 日志")

输出结果:
WARNING:root:warning 日志
ERROR:root:error 日志
CRITICAL:root:critical 日志

3 logging源码分析及流程图

3.1 源码分析

点击查看 - logging源码分析

3.2 流程图

4 基本使用

4.1 basicConfig

可选的参数如下表所示:

示例代码:

# stream设置文件流,繁殖中文乱码
f = open('test.log', 'w', encoding='utf-8')
logging.basicConfig(stream=f, format="%(asctime)s %(name)s:%(levelname)s:%(message)s", datefmt="%d-%M-%Y %H:%M:%S", level=logging.DEBUG)
logging.debug("debug 日志")
logging.info("info 日志")
logging.warning("warning 日志")
logging.error("error 日志")
logging.critical("critical 日志")
f.close()

生成日志:
23-18-2019 00:18:41 root:DEBUG:debug 日志
23-18-2019 00:18:41 root:INFO:info 日志
23-18-2019 00:18:41 root:WARNING:warning 日志
23-18-2019 00:18:41 root:ERROR:error 日志
23-18-2019 00:18:41 root:CRITICAL:critical 日志

当发生异常时,直接使用无参数的 debug()、info()、warning()、error()、critical() 方法并不能记录异常信息,需要设置 exc_info 参数为 True 才可以,或者使用 exception() 方法,还可以使用 log() 方法,但还要设置日志级别和 exc_info 参数。

import logging

logging.basicConfig(filename="test.log", filemode="w", format="%(asctime)s %(name)s:%(levelname)s:%(message)s", datefmt="%d-%M-%Y %H:%M:%S", level=logging.DEBUG)
a = 5
b = 0
try:
    c = a / b
except Exception as e:
    # 下面三种方式三选一,推荐使用第一种
    logging.exception("Exception occurred")
    logging.error("Exception occurred", exc_info=True)
    logging.log(level=logging.DEBUG, msg="Exception occurred", exc_info=True)

4.2 自定义Logger

getLogger函数获取一个Logger对象,使用给定名称对该函数的所有调用都返回相同的记录器实例。这意味着记录器实例永远不需要在应用程序的不同部分之间传递。
Logger 对象可以设置多个 Handler 对象和 Filter 对象,Handler 对象又可以设置 Formatter 对象。Formatter 对象用来设置具体的输出格式,常用变量格式如下表所示:

import logging
import logging.handlers

logger = logging.getLogger("logger")

#设置控制台输出
handler1 = logging.StreamHandler()
#设置文件输出
handler2 = logging.FileHandler(filename="test.log")

#设置输出等级,输出时会和设置的最大值比较
logger.setLevel(logging.DEBUG)
handler1.setLevel(logging.WARNING)
handler2.setLevel(logging.DEBUG)

#添加格式化输出
formatter = logging.Formatter("%(asctime)s %(name)s %(levelname)s %(message)s")
handler1.setFormatter(formatter)
handler2.setFormatter(formatter)
#自定义logger对象添加handler处理对象
logger.addHandler(handler1)
logger.addHandler(handler2)

logger.debug("debug 日志")
logger.info("info 日志")
logger.warning("warning 日志")
logger.error("error 日志")
logger.critical("critical 日志")


终端输出:
2019-11-23 00:38:24,030 logger WARNING warning 日志
2019-11-23 00:38:24,030 logger ERROR error 日志
2019-11-23 00:38:24,030 logger CRITICAL critical 日志

test.log:
2019-11-23 00:38:24,030 logger DEBUG debug 日志
2019-11-23 00:38:24,030 logger INFO info 日志
2019-11-23 00:38:24,030 logger WARNING warning 日志
2019-11-23 00:38:24,030 logger ERROR error 日志
2019-11-23 00:38:24,030 logger CRITICAL critical 日志

NOTE:创建了自定义的 Logger 对象,就不要在用 logging 中的日志输出方法了,这些方法使用的是默认配置的 Logger 对象,否则会输出的日志信息会重复。

4.2 Logger配置

4.2.1 字典格式配置
import logging.config

config = {
    'version': 1,
    'formatters': {
        'simple': {
            'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
        },
        # 其他的 formatter
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'level': 'DEBUG',
            'formatter': 'simple'
        },
        'file': {
            'class': 'logging.FileHandler',
            'filename': 'logging.log',
            'level': 'DEBUG',
            'formatter': 'simple'
        },
        # 其他的 handler
    },
    'loggers':{
        'StreamLogger': {
            'handlers': ['console'],
            'level': 'DEBUG',
        },
        'FileLogger': {
            # 既有 console Handler,还有 file Handler
            'handlers': ['console', 'file'],
            'level': 'DEBUG',
        },
        # 其他的 Logger
    }
}

logging.config.dictConfig(config)
StreamLogger = logging.getLogger("StreamLogger")
FileLogger = logging.getLogger("FileLogger")
4.2.2 配置文件中获取配置信息

logger.ini文件

[loggers]
keys=root,sampleLogger

[handlers]
keys=consoleHandler

[formatters]
keys=sampleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_sampleLogger]
level=DEBUG
handlers=consoleHandler
qualname=sampleLogger
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=sampleFormatter
args=(sys.stdout,)

[formatter_sampleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s

引用:

import logging.config

logging.config.fileConfig(fname='logger.ini', disable_existing_loggers=False)
logger = logging.getLogger("sampleLogger")

5 实战应用

5.1 日志文件按照时间划分或者按照大小划分

# 每隔 1000 Byte 划分一个日志文件,备份文件为 3 个
file_handler = logging.handlers.RotatingFileHandler("test.log", mode="w", maxBytes=1000, backupCount=3, encoding="utf-8")

# 每隔 1小时 划分一个日志文件,interval 是时间间隔,备份文件为 10 个
handler2 = logging.handlers.TimedRotatingFileHandler("test.log", when="H", interval=1, backupCount=10)

5.2 flask logging配置

点击查看 - flask logging官网文档

project_name/conf/config.py

class BaseConfig(object):
    # SECRET_KEY = os.environ.get('SECRET_KEY') or 'awklihdytcbmoq'
    SECRET_KEY = os.urandom(24)
    PERMANENT_SESSION_LIFETIME = timedelta(days=7)

    PROJECT = '伟大的项目'
    VERSION = 'v1.0'
    API_VERSION = 'api/v1.0'
    # 指定json编码格式 如果为False 就不使用ascii编码,
    JSON_AS_ASCII = False
    # 指定浏览器渲染的文件类型,和解码格式;
    JSONIFY_MIMETYPE = "application/json;charset=utf-8"

    DEBUG = True
    TESTING = False
    PROD = False

    # QQ邮箱配置
    MAIL_DEBUG = True  # 开启debug,便于调试看信息
    MAIL_SUPPRESS_SEND = False  # 发送邮件,为True则不发送
    MAIL_SERVER = 'smtp.qq.com'  # 邮箱服务器
    MAIL_PORT = 465  # 端口
    MAIL_USE_SSL = True  # 重要,qq邮箱需要使用SSL
    MAIL_USE_TLS = False  # 不需要使用TLS
    MAIL_USERNAME = '4xxxxxxxx@qq.com'  # 填邮箱
    MAIL_PASSWORD = 'xcxxxxxxxxx'  # 填授权码 -> 百度怎么获取
    FLASK_MAIL_SENDER = 'xxxxxxxx@qq.com'  # 邮件发送方
    FLASK_MAIL_SUBJECT_PREFIX = '{伟大的项目} - 错误日志'  # 邮件标题
    MAIL_DEFAULT_SENDER = '4xxxxxxxx@qq.com'  # 填邮箱,默认发送者
class TestEnvConfig:
    pass
class ProdEnvConfig:
    pass
class DevConfig:
    pass

project_name/init.py

def create_app(config=None, app_name=None, blueprints=None):
    """Create Flask app"""

    if app_name is None:
        app_name = Configs.BaseConfig.PROJECT

    app = Flask(app_name, static_folder='xxx/static',
                template_folder='xxx/tempaltes')
    configure_app(app, config)
    configure_logging(app)

def configure_app(app, config):
    """Configure register app """
    import project_name.conf.config as Configs
    
    # 这里可以用变量设置导入开发环境/测试环境/生产环境不同配置
    app.config.from_object(Configs.BaseConfig)

def configure_logging(app):
    """Configure file(info) and email(error) logging."""

    if app.debug or app.testing:
        # Skip debug and test mode.
        return
    import logging,os
    from logging.handlers import SMTPHandler

    # Set info level on logger, which might be overwritten by handers.
    app.logger.setLevel(logging.INFO)   # 可以设置不同环境log级别  如: app.config['LOGGER_LEVEL']
       
    # Set log storage location
    info_log = os.path.join(app.root_path, "..", "logs", "app-info.log")
    info_file_handler = logging.handlers.RotatingFileHandler(
        info_log, maxBytes=1048576, backupCount=20)
    # handers Level  > logger Level ,might be overwrite
    info_file_handler.setLevel(logging.INFO)  # 可以设置不同环境log级别  如: app.config['HANDLER_LEVEL']
    # set log format
    info_file_handler.setFormatter(logging.Formatter(
        '%(asctime)s %(levelname)s: %(message)s '
        '[in %(pathname)s:%(lineno)d]')
    )
    app.logger.addHandler(info_file_handler)

    ADMINS = ['4xxxxxx@qq.com']
    mail_handler = SMTPHandler(app.config['MAIL_SERVER'],
                               app.config['MAIL_USERNAME'],
                               ADMINS,
                               'O_ops... %s failed!' % app.config['PROJECT'],
                               (app.config['MAIL_USERNAME'],
                                app.config['MAIL_PASSWORD']))
    mail_handler.setLevel(logging.ERROR)
    mail_handler.setFormatter(logging.Formatter(
        '%(asctime)s %(levelname)s: %(message)s '
        '[in %(pathname)s:%(lineno)d]')
    )
    app.logger.addHandler(mail_handler)

伟大的项目 全局使用log

current_app.logger.debug("debug信息")
current_app.logger.warning("warning信息")
current_app.logger.info("info信息")
current_app.logger.error("error信息")
current_app.logger.critical("critical信息")

四、zipfile

1 模块介绍

zipfile模块提供了创建、读取、写入、追加和列出zip文件的工具, 官网链接:https://docs.python.org/3/library/zipfile.html

2 简单示例

2.1 目录:

dirname

2.2 压缩:

def zip_write(zip_dir, zipname):
    """
    压缩文件
    :param zip_dir:压缩路径
    :param zipname:压缩后名称
    :return:
    """
    with zipfile.ZipFile(zipname, "w", zipfile.ZIP_DEFLATED) as zf:
        for path, dirnames, filenames in os.walk(zip_dir):
            for filename in filenames:
                zf.write(os.path.join(path, filename))

zip_write(r'C:UserskekingDesktop	est', 'test.zip')

2.3 压缩结果:

2.4 解压:

def zip_extract(zipname, ex_to=None):
    """
    解压zip文件
    :param zipname:解压文件名称
    :param ex_to:解压存放路径
    :return:
    """
    with zipfile.ZipFile(zipname, "r") as zf:
        for file in zf.namelist():
            zf.extract(file, ex_to)
zip_extract(r'C:UserskekingDesktop	est	est.zip')

3 常用方法总结

file_dir = 'C:UserskekingDesktop	est	est.zip'
zip_obj = zipfile.ZipFile(file_dir)
# 获取zip文档内所有文件的信息,返回一个zipfile.ZipInfo的列表
print(zip_obj.infolist())

# 获取zip文档内所有文件的名称列表
print(zip_obj.namelist())

# 将zip文档内的信息打印到控制台上
print(zip_obj.printdir())

# 上下文管理
with zipfile.ZipFile(file_dir) as zf:

# 压缩
zip_obj.write(filename, arcname=None, compress_type=None, compresslevel=None)
filename -> 文件名称
arcname -> 存档名称,存放目录名称
compress_type -> 通过数字指定压缩方法,ZIP_STORED=0,ZIP_DEFLATED=8,ZIP_BZIP2=12,ZIP_LZMA=14
compresslevel -> 文件写入归档文件时使用的压缩级别

# 解压
zip_obj.extract(member, path=None, pwd=None)
member -> namelist()返回的子成员
path -> 解压文件提取到
pwd -> 加密文件密码

# 多级目录解压
zip_obj.extractall(path=None, members=None, pwd=None)

# 加密
zip_obj.setpassword(pwd)

4 实战应用

4.1 下载zip文件

点击查看 - flask上传下载文件

原文地址:https://www.cnblogs.com/zhangliang91/p/11867308.html