#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
, хорошего дня 🙂