Есть ли pythonic способ регистрации функции с дополнительными значениями без раздувания списка параметров?

#python #logging #functional-programming

#python #ведение журнала #функциональное программирование

Вопрос:

В настоящее время я пытаюсь зарегистрировать функцию в python чистым способом.

Предположим, я хочу зарегистрировать функцию, которая имеет неясный list_of_numbers аргумент as.

 
def function_to_log(list_of_numbers):
    
   # manipulate the values in list_of_numbers ...
   
   return some_result_computed_from_list_of_numbers
 

Когда значения в list_of_numbers обрабатываются в вышеупомянутой функции, я хотел бы зарегистрировать это изменение, но не со значением в неясном list_of_numbers , а со значением с тем же индексом во втором списке list_with_names_for_log .

Что меня раздражает: теперь мне также приходится вводить list_with_names_for_log , что раздувает список аргументов моей функции,

 
def function_to_log(list_of_numbers, list_with_names_for_log):

    # do some stuff like, change value on 3rd index:
    list_of_numbers[3] = 17.4
    log.info('You have changed {} to 17.4'.format(list_with_names_for_log[3]))
    # and so on ...

    return some_result_computed_from_list_of_numbers
 

Я использую несколько из этих списков исключительно для входа в эту функцию.
Есть ли у кого-нибудь идея, как сделать это немного чище?

Комментарии:

1. Находятся ли списки, которые можно использовать для list_with_names_for_log аргумента, в той же области видимости, что и функция? Как вызывающее выражение решает, какой список использовать?

2. Да, списки находятся в материнской функции, которая вызывает function_to_log их в качестве дополнительных аргументов. Какой список использовать жестко запрограммирован function_to_log : для некоторых манипуляций я просто использую один list_with_names для ведения журнала, для некоторых манипуляций — комбинацию этих списков.

3. Вероятно, в вашем вопросе недостаточно подробностей, чтобы обеспечить адекватное решение для ваших обстоятельств. Как указывает Carcigenicate, у вас есть пары списков, которые связаны их индексом, поэтому решение Carcigenicate может решить вашу проблему наличия нежелательного дополнительного параметра функции. Если существует связь между возвращаемыми значениями и аргументами, вы можете проверить возвращаемые значения и выполнить протоколирование в вызывающей функции. Возможно, используйте дескрипторы для значений данных и создайте дескриптор для регистрации при изменении. Или класс NamedData в Carcigenicate может быть разработан для регистрации при изменении.

Ответ №1:

При условии, что имеет смысл группировать данные, я бы сгруппировал пары имя / данные в структуру. В настоящее время у вас есть, по сути, «параллельные списки», которые обычно являются запахом, если вы не используете язык, на котором они являются вашим единственным вариантом.

Можно ввести простой класс данных:

 from dataclasses import dataclass

@dataclass
class NamedData:
    name: str
    data: int
 

Затем:

 def function_to_log(pairs):
    pairs[3].data = 17.4
    log.info('You have changed {} to 17.4'.format(pairs[3].name))
    # and so on ...

return some_result_computed_from_list_of_numbers
 

В качестве образца данных:

 pairs = [NamedData("Some Name", 1), NamedData("Some other name", 2)]
 

И если у вас есть два отдельных списка, его легко адаптировать:

 pairs = [NamedData(name, data) for name, data in zip(names, data_list)]
 

Делайте это только в том случае, если вам обычно нужны оба бита в большинстве мест, где используется каждый список. Группировка поможет очистить код только в том случае, если имя и данные необходимы в большинстве мест, где используется либо одно, либо другое. В противном случае вы просто вводите накладные расходы и раздуваете их в другом месте, чтобы очистить несколько вызовов.