Декоратор класса для предварительной обработки kwargs для других методов класса

#python #class #decorator

#python #класс #декоратор

Вопрос:

У меня есть набор методов в моем CurrencyTools классе, которые я хотел бы иметь возможность принимать kwarg входные данные для разных сокращений валюты. Например, я хочу, чтобы каждый метод принимал либо аргумент ‘usd’, либо аргумент ‘eur’ kwarg . Однако для выполнения своих вычислений функциям требуется, чтобы входные данные были в долларах США, поэтому первой строкой каждого метода было бы преобразование входных данных в валюте, отличной от usd, в usd с использованием self.convert_to_usd() . Этот шаг преобразования кажется дублирующим. Как я могу создать декоратор, который вызывает метод класса self.convert_to_usd , чтобы автоматически выполнить этот этап «предварительной обработки» преобразования и передать преобразованные суммы в долларах США методам? Я не уверен, как декоратор может получить доступ к self для вызова self.convert_to_usd метода.

 class CurrencyTools(object):
   def __init__(self, eurusd_exchange_rate):
      self.eurusd_exchange_rate = eurusd_exchange_rate

   def convert_to_dollars(): 
   #### ?? Decorator ?? ####

   @convert_to_dollars
   def do_calculation(self, **kwargs):
      # usd = self.convert_to_usd(**kwargs) if 'usd' not in kwargs.keys() else kwargs['usd']
      # decorator avoids repeating the line above at the start of every function
      # do calculation with usd

   def convert_to_usd(self, **kwargs):
      return kwargs['eur'] * self.eurusd_exchange_rate

# example use
CurrencyTools(eurusd_exchange_rate=1.1).do_calculation(eur=100)
  

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

1. Декоратор класса — это вызываемый объект, который принимает данный class объект в качестве аргумента и возвращает другой class объект (обычно модифицированную версию того, который был передан). Я думаю, что вам нужен просто обычный декоратор функции (или метода), первым аргументом которого является функция (или метод).

Ответ №1:

Декоратор может заменить оформленный метод другой функцией собственного дизайна. Эта новая функция-оболочка может иметь self аргумент, и механизм Python, который заставляет методы автоматически получать экземпляр, переданный им в качестве первого аргумента, будет работать точно так же, как обычно. Только когда вы переходите self к исходной функции, вам нужно передать ее явно.

Я подозреваю, что вы хотите что-то вроде этого:

 def autoconvert_decorator(method):
    def wrapper(self, *args, eur=None, usd=None, **kwargs): # self gets provided like normal
        if usd is None:
            usd = self.convert_to_usd(eur=eur)
        return method(self, *args, usd=usd, **kwargs) # we have to explicitly pass self here
    return wrapper
  

Вы можете немного изменить это, чтобы продолжать извлекать аргументы ключевого слова usd and / или eur из kwargs , но я думаю, что код будет намного понятнее, если вы явно назовете их в подписи функции-оболочки.

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

1. пожалуйста, используйте @functools.wraps(method) для сохранения документации и других метаданных функций, когда это возможно.

2. @TadhgMcDonald-Дженсен: В данном случае это немного сложнее, поскольку можно рассматривать декоратора как небольшое изменение сигнатуры метода (добавление значения по умолчанию к некоторым аргументам ключевого слова). Я полагаю, что для методов спрашивающего, в которых есть много необходимых элементов **kwargs , это не так уж плохо, хотя я бы предположил, что именованные аргументы (даже если только ключевое слово) лучше.

3. да, извините, я не обязательно обращался к вашему ответу, просто добавив для других людей, которые не знают о wraps существовании, если бы это было несомненно применимо, я бы просто отредактировал ответ. сохранение имени и документации является полезными метаданными, даже если аннотации неприменимы, возможно, используя @functools.wraps(method, ['__module__', '__name__', '__qualname__', '__doc__']) , чтобы аннотации не сохранялись? Я не уверен, в любом случае я считаю, что в любом ответе с использованием оболочек должно быть хотя бы какое-то упоминание о functools.wraps , хорошего дня 🙂