【问题标题】:How to create functions in the global scope based on name, calling a method by same name如何根据名称在全局范围内创建函数,以同名调用方法
【发布时间】:2014-12-24 00:51:58
【问题描述】:

我正在尝试根据函数范围内的logging.Logger 实例的相应方法生成一堆函数。我感兴趣的功能/方法是:

['debug', 'info', 'warning', 'error', 'critical', 'exception']

它们都共享相同的原型。

现在我可以明确地说:

global debug, info, warning, error critical, exception

然后继续def 他们每个人。我检查了它,它工作正常。问题是我必须重复自己。

所以我有一个名为mylog 的logging.Logger 实例,它已经是一个全局变量。以下是我想要的,但通过分别硬编码函数/方法的名称:

global debug
def debug(msg, *args, **kwargs):
    if mylog: mylog.debug(msg, *args, **kwargs)

为了简洁起见,我将只考虑debug 来编写我的示例,跳过其余部分。不幸的是,实际代码稍微复杂一些,因此使用lambda 的要求似乎没有得到满足(单条语句,对吗?)。

所以我的明显想法是在mylog 上使用getattr 和"debug" 的名称,然后简单地调用我在定义的函数中返回的任何内容,并将其按名称添加到globals() dict。示例:

for fct in ['debug']:
    def x(msg, *args, **kwargs):
        if mylog: getattr(mylog, fct)(msg, *args, **kwargs)
    globals()[fct] = x

但我认为这实际上是不合法的,因为我将本应为类方法的方法称为静态方法。尽管如此,这“有效”,但每个输出行后面都有另一行说:None。

所以我想我可以将实例作为第一个参数传递。毕竟这就是self 的意义所在:

for fct in ['debug']:
    def x(msg, *args, **kwargs):
        if mylog: getattr(mylog, fct)(mylog, msg, *args, **kwargs)
    globals()[fct] = x

这给了我:

TypeError: not all arguments converted during string formatting

这与我从中得到的效果相同:

for fct in ['debug']:
    import types
    def x(msg, *args, **kwargs):
        if mylog: types.MethodType(getattr(mylog, fct), mylog)(msg, *args, **kwargs)
    globals()[fct] = x

我基于这个问题的公认答案:Dynamically bind method to class instance in python

应用 Alex Martelli here 的答案会出现同样的错误。

我做错了什么?如何在不重复自己的情况下实现所需的行为?


完整的追溯:

Traceback (most recent call last):
  File "/usr/lib64/python2.6/logging/__init__.py", line 776, in emit
    msg = self.format(record)
  File "/usr/lib64/python2.6/logging/__init__.py", line 654, in format
    return fmt.format(record)
  File "/usr/lib64/python2.6/logging/__init__.py", line 436, in format
    record.message = record.getMessage()
  File "/usr/lib64/python2.6/logging/__init__.py", line 306, in getMessage
    msg = msg % self.args
TypeError: not all arguments converted during string formatting

完整的脚本,基于上面的第一个示例代码:

#!/usr/bin/env python
import sys, logging

def setup_logging():
    global mylog
    mylog = logging.getLogger('foobar')
    mylog.addHandler(logging.StreamHandler())
    for fct in ['debug']:
        def x(msg, *args, **kwargs):
            if mylog: getattr(mylog, fct)(mylog, msg, *args, **kwargs)
        globals()[fct] = x

def main():
    setup_logging()
    debug('TEST LOG MESSAGE')

if __name__ == '__main__':
    sys.exit(main())

【问题讨论】:

  • 能否包含“并非所有参数”错误的完整回溯?
  • @NedBatchelder:不知道这会有什么帮助,因为这是在库模块中,但你去吧:)

标签: python logging dry python-2.6


【解决方案1】:

在我看来这应该可行:

debug = mylog.debug

或:

debug, info, warning, error, critical, exception = (
    mylog.debug, mylog.info, mylog.warning, 
    mylog.error, mylog.critical, mylog.exception)

动态设置:

for fct in ('debug', 'info', 'warning'):
    globals()[fct] = getattr(mylog, fct)

你的 for 循环 for fct in ('debug'): 正在迭代字符串 'debug',你需要一个逗号 for fct in ('debug',): 来使它成为一个元组。

还要注意一个结构,例如:

for fct in ['debug']:
    def x(msg, *args, **kwargs):
        if mylog: getattr(mylog, fct)(msg, *args, **kwargs)
    globals()[fct] = x

定义一个函数在调用时使用`fct`的当前值。您需要额外的间接级别来处理多个日志记录方法。例如

def make_log(fct):
    def x(msg, *args, **kwargs):
        if mylog: getattr(mylog, fct)(msg, *args, **kwargs)
    return x

for fct in ['debug','info']:
    globals()[fct] = make_log(fct)

【讨论】:

  • 感谢您的宝贵时间。但是,这回避了如何根据名称(或“动态”)执行此操作的问题。但它是不那么冗长,这也很酷。
  • 天啊。我才发现。我自己的示例使用了几个条目。列表也可以使用或('debug', )。现在试试你的方法。更正了我的问题中的示例。
  • 你设置它的方式当然是动态的。但没有考虑到我在代码中需要一些额外的东西,在我的问题中由if mylog: 表示。仍然为您的所有努力 +1。
  • 太棒了,谢谢。根据我的理解,我没有使用其他抽象层次的事实让我感到困惑。
猜你喜欢
  • 2012-07-05
  • 2013-12-12
  • 1970-01-01
  • 1970-01-01
  • 2013-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-29
相关资源
最近更新 更多