94 Глава 3 •
Эффективные функции
Это также дает вам возможность модифицировать аргументы перед тем,
как вы передадите их дальше. Вот пример:
def foo(x, *args, **kwargs):
kwargs['имя'] = 'Алиса'
new_args = args + ('дополнительный', )
bar(x, *new_args, **kwargs)
Данный прием может быть полезен для создания производных классов
и написания оберточных функций. Например, он может применяться
для расширения поведения родительского класса без необходимости
повторять полную сигнатуру его конструктора в дочернем классе. Это
может быть довольно удобно, если вы работаете с API, который может
измениться за пределами вашего контроля:
class Car:
def __init__(self, color, mileage):
self.color = color
self.mileage =
mileage
class AlwaysBlueCar(Car):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.color = 'синий'
>>> AlwaysBlueCar('зеленый', 48392).color
'синий'
Конструктор класса
AlwaysBlueCar
просто передает все аргументы в свой
родительский класс и затем переопределяет внутренний атрибут. Это оз-
начает, что если конструктор родительского класса изменится, то велика
вероятность того, что
AlwaysBlueCar
будет по-прежнему функционировать
как было задумано.
Оборотной стороной здесь является то, что конструктор
AlwaysBlueCar
теперь имеет довольно бесполезную сигнатуру, — мы не узнаем, какие
аргументы он ожидает, не заглянув в родительский класс.
Как правило, вы не будете использовать этот прием со своими собствен-
ными иерархиями классов. Более вероятный сценарий будет такой, что вы
захотите изменить или переопределить поведение в некотором внешнем
классе, которым не управляете.
3 .4 . Веселье с *args и **kwargs
95
Но это всегда опасная территория, поэтому лучше соблюдать осторож-
ность (иначе вскоре у вас,
возможно, появится еще одна причина вос-
кликнуть «аррррг!»).
Еще один сценарий, где этот прием будет потенциально полезен, — на-
писание оберточных функций, таких как декораторы. Там вы также захо-
тите принимать произвольные аргументы, которые будут переправляться
в обернутую функцию.
И если мы можем сделать это без необходимости копипастить сигнатуру
оригинальной функции, то, возможно, сопровождение станет удобнее:
def trace(f):
@functools.wraps(f)
def decorated_function(*args, **kwargs):
print(f, args, kwargs)
result = f(*args, **kwargs)
print(result)
return decorated_function
@trace
def greet(greeting, name):
return '{}, {}!'.format(greeting, name)
>>> greet('Привет', 'Боб')
('Привет', 'Боб') {}
Do'stlaringiz bilan baham: