Программно определить статические методы из списка

У меня есть объект с базовым методом log(text, level), список levels, и я вручную определяю каждый метод следующим образом:

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

(Где <level> должен быть заменен одним из элементов списка) Как я мог сделать это программно?

Кроме того, мой список levels на самом деле является словарем, а <level> — ключом, но я знаю, как его перебрать.

А) модуль logging имеет именно это и б) методы определяются там вручную по уважительным причинам (см. PEP-20).

Klaus D. 30.01.2019 12:43

также посмотрите на атрибуты python.

Sufiyan Ghori 30.01.2019 12:45
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
2
98
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы можете использовать подобную функцию setattr для каждого уровня, обратите внимание на использование функции-оболочки staticmethod.

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

Обновлено: Вы должны добавить level=level в объявление лямбда, иначе все лямбды ссылаются на один и тот же level, который назначен последним.

Пробовал, все заработало, кроме '<level>' внутри определения функции. Как я могу получить к нему доступ вне определения функции?

ewen-lbh 30.01.2019 13:19

Я пробовал это: for level in LOG_TYPES: setattr(log, level, staticmethod(lambda text: log.log(text, level))) И он всегда использует последний элемент моего списка LOG_TYPES, независимо от определенной функции:/

ewen-lbh 30.01.2019 13:20

Если вы хотите сгенерировать код, используйте что-то вроде мако или Джиндзя. Вы хотите, чтобы генерация вашего кода и фактический код, который выполняется вашей программой, были разделены. В основном это связано с тем, что код, сгенерированный на 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')

Лучше, но этот подоконник выглядит довольно грязно. Дальнейшие линтеры по-прежнему не могут помочь вам с опечатками в именах функций и атрибутов. Насколько известно линтеру, Logger.info — такая же опечатка, как и Logger.inf.

С помощью mako вы можете написать файл шаблона следующим образом:

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

    % endfor

Здесь уродство шаблона заключено в файле шаблона, а сгенерированный код выглядит как обычный, полностью выраженный код.

Другие вопросы по теме