Argparse和Logging


Argparse和Logging

ArgparseLogging是Python实验中常用的两个模块. 之前没有整理过, 特此整理.

Argparse

Argparse是用来解析Python命令行标准库.

框架

大致使用框架如下:

import argparse
parser = argparse.ArgumentParser(description="This is the description for this python script")
'''
Add some arugments...
'''
args = parser.parse_args()

在命令行中调用脚本时可以附加参数:

python argparse_test.py -n anning

总体来说, 流程如下:

  1. 需要导入Argparse.
  2. 创建一个parser, 并对其添加各种参数.
  3. 使用parse_args()解析参数.
  4. 使用Python命令行时可以附加相应的参数.

添加参数

添加参数统一使用函数parser.add_argument(), 在该函数中可以设置参数的各种属性约束. 在下文会逐一说明.

获取参数

设定的argument可以作为属性, 在解析参数后, 使用args.argument_name来获取.

位置参数

位置参数当然是必选的参数, 在启动Python脚本时必须按照位置依次输入. 例如:

parser.add_argument('name', type=str, help='Your name')

添加了参数name, 并将其设置为str类型的变量.

可选参数

顾名思义, 在执行脚本时可选参数可能会被用户附加, 也有可能不附加. 参数的添加方式与位置参数类似, 必须在参数名前加-. 约定上-后应只跟单个字母, 为某个参数的缩写, --后跟参数的全拼. 例如:

parser.add_argument('-n', type=str, help='Your name')
parser.add_argument('--age', type=int, help='Your age')

添加了两个可选参数nage, 分别为strint类型.

当然, 想要同时将缩写和全拼指向同一个变量也是可以的:

parser.add_argument('-a', '--age', type=int, help='Your age')

此时, 你既可以通过-a, 也可以通过--age来定义年龄.

如果你同时指定了参数的缩写和全拼, 那么在访问该参数值时, 必须通过全拼来访问.

默认值和类型

default设定字段的默认值, type可以指定附加参数后将其转化为什么数据类型. 例如:

parser.add_argument('--name', type=str, default='anning', help='Your name')

添加了一个参数name, 当命令行中不显式声明时缺省值为'anning', 类型为字符串.

别名

dest可以指定该参数所对应的args别名, 在Python中获取该参数可以通过指定的别名来获取. 例如:

parser.add_argument('--name', dest='user_name', type=str, help='Your name')

在没指定别名时, 访问参数name可以通过args.name获取, 而指定别名后, 只能通过args.user_name来获取.

必须参数

required设定参数是否必须填入. 否则会提示该参数没有指定, 因此可以将可选参数转化为必选参数.

动作

action一般常用于可选参数, 例如有参数--verbose时, 该属性设置为True:

parser.add_argument('--verbose', action='store_true')

此时, 如果在调用脚本时加上了--verbose, 那么在解析后, 对应的args.verbose就为True.

当然这只是action其中一种用法, 更多请参见节尾处的链接.

值约束

choices可以设定用户添加参数时, 参数的取值范围. 例如:

parser.add_argument('--name',choices=['anning', 'daning'] type=str, help='Your name')

那么用户只能在'anning''daning'之间选择name.

多参数设定

nargs='N'可以将命令行的N个参数汇总到一个列表中, nargs='+'nargs='*'能将所有当前参数汇聚到一个列表中. 例如:

parser.add_argument('--salary', dest='salary', default=[1, 2, 5], nargs="+", type=int)

在调用脚本时, 能够传入多个符合条件的值, 它们将以列表的形式共同存在.

互斥参数组

有的时候不希望用户按照自己想象之外的使用方法传入参数, 就需要用到互斥参数. 例如:

ab_group = parser.add_mutually_exclusive_group()
ab_group.add_argument('-a')
ab_group.add_argument('-b')

那么, 在调用Python脚本时, 两个参数是不能同时启用的, 否则会报错.

Tips

var(args)能将解析好后的内容直接转为字典.

在使用Notebook时, 与argparse相关的代码以.py形式存在, 不方便直接从里面把参数扒下来, 而模型必须使用args初始化.

这时可以使用pickle库将args直接以对象的形式保存下来, 然后再使用pickle.load在Notebook中重新读取出来, 再对模型初始化. 示例:

import pickle
# 保存
with open('args.pkl', 'wb') as f:
   pickle.dump(args, f)

# 读取
with open('args.pkl', 'rb') as f:
   args = pickle.load(f)

上述列举的只是我遇到的关于Argparse的用法, 可能说的比较碎而且不全面. 更多内容请参见Python官方文档argparse - 命令行选项与参数解析(译).

Logging

Logging可以记载Python脚本运行的过程, 即记录日志信息. 其实实验中用到的Logging非常简单.

Logger

简单配置

如果我们只是想简单的记录日志, 不考虑程序的后续维护问题, 那么我们只需要简单的进行logger的初始化. 例如:

logging.basicConfig(level=logging.DEBUG,
                    filename='output.log',
                    datefmt='%Y/%m/%d %H:%M:%S',
                    format='%(asctime)s - %(name)s - %(levelname)s - %(lineno)d - %(module)s - %(message)s')
logger = logging.getLogger("process_name")

basicConfig参数

  • filename: 即日志输出的文件名, 如果指定了这个信息之后, 实际上会启用 FileHandler, 而不再是 StreamHandler, 这样日志信息便会输出到文件中了.
  • filemode: 这个是指定日志文件的写入方式, 有两种形式, 一种是w, 一种是a, 分别代表清除后写入和追加写入.
  • format: 指定日志信息的输出格式, 详细格式会在后面补出.
  • datefmt: 指定时间的输出格式.
  • style: 如果 format 参数指定了, 这个参数就可以指定格式化时的占位符风格, 如 %、{、$ 等.
  • level: 指定日志输出的类别, 程序会输出大于等于此级别的信息.
  • stream: 在没有指定 filename 的时候会默认使用 StreamHandler, 这时 stream 可以指定初始化的文件流.
  • handlers: 可以指定日志处理时所使用的 Handlers, 必须是可迭代的.

配置文件配置

一般情况下, 为了把配置写活, 人们都会将配置写入配置文件, 在记录日志的时候, 读取配置文件中的配置, 方便管理.

无论以何种方式读取文件, 例如yaml, json, 只要读取成Python的字典, 就可以完成初始化, 例如:

config_dict = json.load(config_path)
logging.config.dictConfig(config_dict)
logger = logging.getLogger("process_name")

在这里, 以json作为示范, 在配置文件中写入的内容有:

{
    "version": 1,
    "disable_existing_loggers": false,
    "formatters": {
        "simple": {
            "format": "%(asctime)s - %(name)s - [%(levelname)s] - %(message)s"
        }
    },

    "handlers": {
        "file_handler": {
            "class": "logging.FileHandler",
            "level": "DEBUG",
            "formatter": "simple",
            "filename": "python_logging.log",
            "encoding": "utf8"
        }
    },

    "root": {
        "level": "DEBUG",
        "handlers": ["file_handler"]
    }
}

Level

日志记录分为五个级别, 分别为CRITICAL > ERROR > WARNING > INFO > DEBUG.

它们分别可以通过下述命令来记录在日志中:

logger.debug("mess")
logger.info("mess")
logger.warning("mess")
logger.error("mess""mess")
logger.critical("mess")

logger中的level参数可以指定程序输出的信息级别.

Handler

Handler处理日志的方法. 我们可以不使用basicConfig来配置logger, 而是单独对logger指定处理方法:

import logging

logger = logging.getLogger("process_name")
logger.setLevel(level=logging.INFO)
handler = logging.FileHandler('output.log')
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)

我们常用的是FileHandler, 这样能将日志以文件形式导出. 除此以外, 还有如下Handler:

  • StreamHandler: logging.StreamHandler, 日志输出到流, 可以是 sys.stderr, sys.stdout 或者文件.
  • FileHandler: logging.FileHandler, 日志输出到文件.
  • BaseRotatingHandler: logging.handlers.BaseRotatingHandler, 基本的日志回滚方式.
  • RotatingHandler: logging.handlers.RotatingHandler, 日志回滚方式, 支持日志文件最大数量和日志文件回滚.
  • TimeRotatingHandler: logging.handlers.TimeRotatingHandler, 日志回滚方式, 在一定时间区域内回滚日志文件.
  • SocketHandler: logging.handlers.SocketHandler, 远程输出日志到 TCP/IP sockets.
  • DatagramHandler: logging.handlers.DatagramHandler, 远程输出日志到 UDP sockets.
  • SMTPHandler: logging.handlers.SMTPHandler, 远程输出日志到邮件地址.
  • SysLogHandler: logging.handlers.SysLogHandler, 日志输出到 syslog.
  • NTEventLogHandler: logging.handlers.NTEventLogHandler, 远程输出日志到 Windows NT/2000/XP 的事件日志.
  • MemoryHandler: logging.handlers.MemoryHandler, 日志输出到内存中的指定 buffer.
  • HTTPHandler: logging.handlers.HTTPHandler, 通过”GET” 或者”POST” 远程输出到 HTTP 服务器.

实际上, 我们可以给logger添加多个Handler:

import logging
from logging.handlers import HTTPHandler
import sys

logger = logging.getLogger("process_name")
logger.setLevel(level=logging.DEBUG)

# StreamHandler
stream_handler = logging.StreamHandler(sys.stdout)
stream_handler.setLevel(level=logging.DEBUG)
logger.addHandler(stream_handler)

# FileHandler
file_handler = logging.FileHandler('output.log')
file_handler.setLevel(level=logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)

# HTTPHandler
http_handler = HTTPHandler(host='localhost:8001', url='log', method='POST')
logger.addHandler(http_handler)

# Log
logger.info('This is a log info')
logger.debug('Debugging')
logger.warning('Warning exists')
logger.info('Finish')

Formatter

在对日志格式化输出时, 可以不借助basicConfig来全局化输出格式, 使用Formatter灵活单独配置:

logger = logging.getLogger("process_name")
logger.setLevel(level=logging.WARN)
formatter = logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y/%m/%d %H:%M:%S')
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logger.addHandler(handler)

更多的信息输出格式如下:

  • %(levelno) s : 打印日志级别的数值.
  • %(levelname) s : 打印日志级别的名称.
  • %(pathname) s : 打印当前执行程序的路径, 其实就是 sys.argv [0].
  • %(filename) s : 打印当前执行程序名.
  • %(funcName) s : 打印日志的当前函数.
  • %(lineno) d : 打印日志的当前行号.
  • %(asctime) s : 打印日志的时间.
  • %(thread) d : 打印线程 ID.
  • %(threadName) s : 打印线程名称.
  • %(process) d : 打印进程 ID.
  • %(processName) s : 打印线程名称.
  • %(module) s : 打印模块名称.
  • %(message) s : 打印日志信息.

上述内容也并非全部的使用方法, 参考了Python官方文档Python 中 logging 模块的基本用法.


文章作者: DaNing
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 DaNing !
评论
 上一篇
PPKE: Knowledge Representation Learning by Path-based Pre-training PPKE: Knowledge Representation Learning by Path-based Pre-training
本文前置知识: BERT(Transformer Encoder). PPKE: Knowledge Representation Learning by Path-based Pre-training本文是论文PPKE: Know
2021-01-18
下一篇 
RREA: Relational Reflection Entity Alignment RREA: Relational Reflection Entity Alignment
本文前置知识: GNN Relational Reflection Entity Alignment本文是论文Relational Reflection Entity Alignment的阅读笔记和个人理解. 这是我第一次接触关于实
2020-12-30
  目录