以编程方式从列表中定义静态方法

Define static methods from list programmatically

我有一个带有基本方法 log(text, level) 的对象,一个 levels 的列表,我手动定义每个方法如下:

@staticmethod
def <level>(text):
     log.log(text, '<level>')

(其中 <level> 将替换为列表中的一项) 我怎么能以编程方式做到这一点?

此外,我的 levels 列表实际上是一个字典,<level> 是关键,但我知道如何迭代它。

您可以对每个级别使用这样的 setattr 函数,注意使用 staticmethod 包装函数。

for level in LOG_TYPES:
    setattr(A, level, staticmethod(lambda text, level=level: log.log(text, level)))

编辑: 您必须在 lambda 声明中添加 level=level,否则所有 lambda 都引用相同的 level,这恰好是最后一个分配的。

如果您想生成代码,请使用类似 mako or jinja 的代码。您希望将代码的生成与程序运行的实际代码分开。这主要是因为 python 中生成的代码更难调试。

例如,在 Python 中,您可以像这样生成代码:

from textwrap import dedent

class Logger:
    for level in ('debug', 'info'):
        exec(dedent("""
            @staticmethod
            def {level}(text):
                log.log(text, {level!r})
        """.format(level=level)))

Logger.info('test')

但是如果发生异常,您的堆栈跟踪将会令人困惑,例如。

Traceback (most recent call last):
  File "C:\Users\User\Documents\python\a.py", line 18, in <module>
    Logger.info('test')
  File "<string>", line 4, in info <-- what is <string> and where can you find it?
NameError: name 'log' is not defined

相反,您可以尝试:

class Logger:
    def gen_log_func(level):
        def _log(text):
            log.log(text, level)
        _log.__name__ = level
        return _log

    for level in ('debug', 'info'):
        locals()[level] = staticmethod(gen_log_func(level))

    del gen_log_func

Logger.info('test')

好多了,但是这个窗台看起来很乱。更多的 linters 仍然无法帮助您解决函数和属性名称中的拼写错误。所有 linter 都知道,Logger.infoLogger.inf.

一样都是错字

使用 mako,您可以编写如下模板文件:

<%
    levels = 'debug', 'info'
%>
class Logger:
    % for level in levels:
    @staticmethod
    def ${level}(text):
        log.log(text, ${repr(level)})

    % endfor

这里模板的丑陋之处被模板文件捕获,生成的代码看起来就像正常的、完全表达的代码。