7

假设我有一个主程序 (test.py) 和一个小实用程序 (test_utils.py),它具有由主程序调用的辅助函数。我想通过传递一个debug_flag布尔值来打开代码中的调试语句,该布尔值是通过argparse.

现在我希望我的test_utils.py程序中的函数也可以根据debug_flag. 我总是可以将debug_flag参数添加到每个函数定义中,test_utils.py并在调用函数时传递参数,但是这里有更好的方法,比如创建 debug_flag一个全局变量吗?但是,如果我确实声明debug_flag为 global from test.py,那将如何导入 test_utils.py

这里最优雅/ Pythonic的方法是什么?

测试.py:

import argparse
from test_utils import summation

def main():
    args = get_args()
    debug_flag = True if args[debug] == 'True' else False
    print summation(5, 6, 7)

def get_args():
    parser = argparse.ArgumentParser(description='Test program')
    parser.add_argument('-d','--debug', help='Debug True/False', default=False)
    args = vars(parser.parse_args())
    return args

test_utils.py:

from test import debug_flag

def summation(x, y, z):
    if debug_flag:
        print 'I am going to add %s %s and %s' % (x, y, z)
    return x + y + z

EDIT1:澄清 - 如果我通过 argparse 传入调试标志并因此将 debug_flag 设置为 True - 这将如何传播到内部的函数test_utils.py

EDIT2:根据@joran-beasley 的建议,这就是我所拥有的。

测试.py:

import argparse
import logging
from test_utils import summation

def main():
    args = get_args()
    logging.getLogger("my_logger").setLevel(logging.DEBUG if args['debug'] == 'True' else logging.WARNING)
    print summation(5, 6, 7)

def get_args():
    parser = argparse.ArgumentParser(description='Test program')
    parser.add_argument('-d','--debug', help='Debug True/False', required=True)
    args = vars(parser.parse_args())
    return args

main()

test_utils.py

import logging

log = logging.getLogger('my_logger')

def summation(x, y, z):
    log.debug('I am going to add %s %s and %s' % (x, y, z))
    return x + y + z

当我运行 test.py 时,我得到:

$ python test.py -d True
No handlers could be found for logger "my_logger"
18
4

3 回答 3

5

使用日志记录

# this logger will always now be logging.DEBUG level
logging.getLogger("my_logger").setLevel(logging.DEBUG if args.debug else logging.WARNING)

然后使用

log = logging.getLogger("my_logger")
...
log.warn("some text that should always be seen!")
log.debug("some debug info!")

然后,您可以执行具有多个日志记录级别的操作

log_level = logging.WARNING
if args.verbose > 0:
   log_level = logging.INFO
elif args.verbose > 3:
   log_level = logging.DEBUG

如果由于某种原因您需要检索 currentEffectiveLogLevel(在大多数情况下您真的不应该......只需log.debug在需要调试级别输出时使用)

logging.getLogger("my_logger").getEffectiveLevel()

[编辑澄清]

log = logging.getLogger('my_logger')

def summation(x, y, z):
   log.debug('I am going to add %s %s and %s' % (x, y, z)) # will not print if my_logger does not have an effective level of debug
   return x + y + z

print(summation(2, 3, 4))
log.setLevel(logging.DEBUG)
print(summation(4, 5, 6))

或者你可以写一个辅助函数

def is_debug():
    return logging.getLogger("my_logger").getEffectiveLevel() == logging.DEBUG

粗略的你总是可以做一些可怕的废话,比如将它写入一个平面文件并读取它或使用真正的全局变量(比你想象的要难,而且要担心很多边缘情况)

于 2018-04-11T17:56:02.187 回答
1

你可以通过传递一个可变的变量在 Python 中实现“包范围”的全局。在这些情况下,我最喜欢的方法是创建一个自定义Flags类。Flags然后,您在所有模块之间共享一个实例,其属性的功能类似于全局变量。这是您发布的代码的示例:

test_utils.py

__all__ = ['flags', 'summation']

class Flags(object):
    def __init__(self, *items):
        for key,val in zip(items[:-1], items[1:]):
            setattr(self,key,val)

flags = Flags('debug', False)

def summation(x, y, z):
    if flags.debug:
        print 'I am going to add %s %s and %s' % (x, y, z)
    return x + y + z

测试.py

#!/usr/bin/env python2
import argparse
import sys

from test_utils import summation, flags

def main():
    args = get_args()
    flags.debug = args['debug']
    print summation(5, 6, 7)

def get_args():
    parser = argparse.ArgumentParser(description='Test program')
    parser.add_argument('-d','--debug', action='store_true', help='Debug True/False', default=False)
    args = vars(parser.parse_args())
    return args

if __name__=='__main__':
    main()

我将标志移动到 test_utils.py 以避免循环导入问题,但这不会影响任何事情。一个更强大的解决方案(适用于较大的项目)将有一个单独的 config.py (或其他东西)模块,在该模块中进行flags初始化。

于 2018-04-11T17:58:31.520 回答
0

简单的回答。在主文件中分配一个字典。然后通过所有其他模块导入它。

测试.py

debug_flag = {'setting' : False}

test_utils.py

from test import debug_flag

def print_flag():
    print(debug_flag['setting'])

def set_flag(setting):
    debug_flag['setting'] = setting

set_flag(True)
print_flag()

现在,无论何时您从主模块导入 debug_flag,一旦您处理了 arg 解析,它将具有您设置标志的任何设置。然后,您可以随时更改此设置,随后的调用将获取更改。

于 2018-04-11T19:04:47.760 回答