Декораторы с параметрами?
У меня возникла проблема с передачей переменной insurance_mode
в декораторе. Я пытался сделать это следующим образом:
@execute_complete_reservation(True)
def test_booking_gta_object(self):
self.test_select_gta_object()
Но, к сожалению, это не сработало. Возможно, есть более подходящее решение этой проблемы.
Вот код декоратора:
def execute_complete_reservation(test_case, insurance_mode):
def inner_function(self, *args, **kwargs):
self.test_create_qsf_query()
test_case(self, *args, **kwargs)
self.test_select_room_option()
if insurance_mode:
self.test_accept_insurance_crosseling()
else:
self.test_decline_insurance_crosseling()
self.test_configure_pax_details()
self.test_configure_payer_details()
return inner_function
Как правильно передать переменную insurance_mode
в декораторе, чтобы это работало корректно?
5 ответ(ов)
Я хотел бы представить идею, которая, на мой взгляд, довольно элегантна. Решение, предложенное t.dubrownik, показывает шаблон, который всегда одинаков: вам нужен трехуровневый обертка независимо от того, что делает декоратор.
Поэтому я подумал, что это задача для мета-декоратора, то есть декоратора для декораторов. Поскольку декоратор — это функция, он на самом деле работает как обычный декоратор с аргументами:
def parametrized(dec):
def layer(*args, **kwargs):
def repl(f):
return dec(f, *args, **kwargs)
return repl
return layer
Это может быть применено к обычному декоратору, чтобы добавить параметры. Допустим, у нас есть декоратор, который удваивает результат функции:
def double(f):
def aux(*xs, **kws):
return 2 * f(*xs, **kws)
return aux
@double
def function(a):
return 10 + a
print(function(3)) # Вывод: 26, то есть 2 * (10 + 3)
С помощью @parametrized
мы можем создать универсальный декоратор @multiply
с параметром:
@parametrized
def multiply(f, n):
def aux(*xs, **kws):
return n * f(*xs, **kws)
return aux
@multiply(2)
def function(a):
return 10 + a
print(function(3)) # Вывод: 26
@multiply(3)
def function_again(a):
return 10 + a
print(function(3)) # Продолжает выводить 26
print(function_again(3)) # Вывод: 39, то есть 3 * (10 + 3)
Как правило, первым параметром параметризованного декоратора является функция, а остальные аргументы будут соответствовать параметрам этого параметризованного декоратора.
Интересным примером использования может быть декоратор с типобезопасной проверкой:
import itertools as it
@parametrized
def types(f, *types):
def rep(*args):
for a, t, n in zip(args, types, it.count()):
if type(a) is not t:
raise TypeError('Значение %d не имеет типа %s. Вместо него %s' %
(n, t, type(a))
)
return f(*args)
return rep
@types(str, int) # arg1 - str, arg2 - int
def string_multiply(text, times):
return text * times
print(string_multiply('hello', 3)) # Вывод: hellohellohello
print(string_multiply(3, 3)) # Ошибка TypeError
Наконец, отмечу, что здесь я не использую functools.wraps
для функций-оберток, но я бы рекомендовал использовать его всегда.
Этот код представляет собой реализацию декоратора в Python, который проверяет, что все переданные аргументы являются целыми числами, перед тем как выполнять основную функцию. Давайте разберем его шаг за шагом.
Определение декоратора
Функция decorator(argument)
принимает один аргумент (argument
), который будет использоваться для умножения результата основной функции. Внутри нее определена другая функция real_decorator(function)
, которая принимает функцию, для которой мы хотим применить декоратор.
def decorator(argument):
def real_decorator(function):
def wrapper(*args):
for arg in args:
assert type(arg) == int, f'{arg} is not an integer'
result = function(*args)
result = result * argument
return result
return wrapper
return real_decorator
Использование декоратора
Мы применяем декоратор @decorator(2)
к функции adder(*args)
, которая просто складывает все переданные ей аргументы.
@decorator(2)
def adder(*args):
sum = 0
for i in args:
sum += i
return sum
Пример вызова
Теперь, когда мы вызываем adder(2, 3)
, происходит следующее:
- Декоратор проверяет, что оба аргумента (
2
и3
) являются целыми числами. - Функция
adder
возвращает сумму2 + 3
, т.е.5
. - Результат
5
умножается наargument
, равный2
, в результате чего мы получаем10
.
adder(2, 3) # результат: 10
Обработка неправильного типа
Если мы вызовем adder('hi', 3)
, декоратор выполнит проверку типов и выбросит исключение AssertionError
, поскольку строка 'hi'
не является целым числом. Вывод будет следующим:
adder('hi', 3)
Приведет к:
AssertionError: hi is not an integer
Заключение
Таким образом, декоратор позволяет не только модифицировать поведение функции, но и добавлять проверки перед вызовом основной логики, что делает код более устойчивым.
Ваша функция-декоратор содержит несколько моментов, которые требуют внимания. Вот перевод на русский язык в стиле ответа на StackOverflow:
Так просто!
Ваш декоратор real_decorator
выглядит хорошо, но есть небольшая ошибка в том, как вы передаете аргументы в декорируемую функцию. Давайте исправим это.
Вот исправленный код:
def real_decorator(any_number_of_arguments):
def pseudo_decorator(function_to_be_decorated):
def real_wrapper(*function_arguments): # Используем * для примера с произвольным количеством аргументов
print(function_arguments)
result = function_to_be_decorated(*function_arguments) # Передаем аргументы в декорируемую функцию
return result
return real_wrapper
return pseudo_decorator
Теперь, чтобы использовать ваш декоратор, убедитесь, что функция some_function
принимает правильные аргументы:
@real_decorator("some_argument") # Пример передачи аргумента
def some_function(*function_arguments): # Используем * для поддержки произвольного количества аргументов
return "Any"
Теперь, если вы вызовете some_function
, декоратор будет корректно обрабатывать аргументы. Таким образом, ваш код будет работать так, как ожидается!
Отличные ответы выше. Этот пример также иллюстрирует использование @wraps
, который берет строку документации и имя функции из оригинальной функции и применяет их к новой обернутой версии:
from functools import wraps
def decorator_func_with_args(arg1, arg2):
def decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
print("Перед вызовом оригинальной функции с аргументами декоратора:", arg1, arg2)
result = f(*args, **kwargs)
print("Завершен вызов оригинальной функции")
return result
return wrapper
return decorator
@decorator_func_with_args("foo", "bar")
def hello(name):
"""Функция, которая выводит приветствие для указанного имени.
"""
print('hello ', name)
return 42
print("Начинаем выполнение скрипта..")
x = hello('Bob')
print("Значение x:", x)
print("Докстрока обернутой функции:", hello.__doc__)
print("Имя обернутой функции:", hello.__name__)
Вывод будет следующим:
Начинаем выполнение скрипта..
Перед вызовом оригинальной функции с аргументами декоратора: foo bar
hello Bob
Завершен вызов оригинальной функции
Значение x: 42
Докстрока обернутой функции: Функция, которая выводит приветствие для указанного имени.
Имя обернутой функции: hello
Таким образом, @wraps
помогает сохранить метаданные оригинальной функции, что очень полезно для отладки и документирования.
В вашем коде используется декоратор multiply
, который позволяет изменять поведение функции sum
в зависимости от переданного аргумента. Декоратор может принимать один аргумент или вообще не принимать никаких аргументов.
Пример использования декоратора
Вот как это работает в вашем примере:
- Использование декоратора с аргументом:
@multiply(5) # Здесь мы передаем 5
def sum(num1, num2):
return num1 + num2
result = sum(4, 6)
print(result) # Вывод: 50
Как видно из кода, когда вы вызываете sum(4, 6)
, функция сначала вычисляет сумму, получая 10, а затем умножает результат на 5, в результате чего получаем 10 * 5 = 50
.
- Использование декоратора без аргументов:
@multiply() # Здесь не передаем аргументы
def sum(num1, num2):
return num1 + num2
result = sum(4, 6)
print(result) # Вывод: 10
В этом случае, поскольку мы не передали аргумент, декоратор использует значение по умолчанию для num
, равное 1. Таким образом, результат остается 10, так как 10 * 1 = 10
.
- Использование декоратора без скобок:
@multiply # Здесь тоже не передаем аргументы
def sum(num1, num2):
return num1 + num2
result = sum(4, 6)
print(result) # Вывод: 10
Здесь поведение аналогично предыдущему примеру. Декоратор multiply
без скобок также ведет себя так же, как и с пустыми скобками, возвращая итоговую сумму, равную 10.
Заключение
Таким образом, ваш декоратор multiply
корректно обрабатывает различные варианты передачи аргументов, что позволяет гибко изменять поведение функции sum
. Это демонстрирует мощь и гибкость использования декораторов в Python.
Установка значения по умолчанию для параметра функции в JavaScript
Как создать декораторы функций и объединить их?
Передача параметров в функцию Bash
Как получить возвращаемое значение из потока?
Определение имени функции изнутри самой функции