Декоратор метода класса - получить аргументы для функции, изменить и передать дальше в функцию
Я ещё нуб в питоне, если вопрос покажется смешным, но разобраться не могу и ответа ни здесь ни вообще так и не нашел
Пишу класс ActivkaBackup() в котором есть несколько методов обращающихся к FTP серверам (получение списка файлов, списка файлов по фильтру, содержимого файла и т.п.) использую ftplib.FTP для подключения требуется host,user,passwd,acct но у меня 2 сервера, основной и резервный, разные хосты разные учетки. получаю их из ini файла при инициализации объекта класса
self.main_backup_server['ftp_user'] = myini.main_backup_server['user']
self.second_backup_server['ftp_user'] = myini.second_backup_server['user']
если методы в общем случае имеют вид someMetods(self, *args, **kwargs)
как сделать декоратор, который бы проверял, существует ли kwargs['second'] = True
в зависимости от этого добавлял в kwargs необходимые элементы и вызывал бы someMetods(self, *args, **kwargs) с уже обновленными kwargs.
Написал декоратор:
def _set_ftp_var(func):
def wrapper(*args, **kwargs):
if kwargs.get('second'):
kwargs['host'] = self.second_backup_server['name']
kwargs['user'] = self.second_backup_server['ftp_user']
kwargs['passwd'] = self.second_backup_server['ftp_password']
kwargs['ftp_root'] = self.second_backup_server['ftp_root']
else:
kwargs['host'] = self.main_backup_server['name']
kwargs['user'] = self.main_backup_server['ftp_user']
kwargs['passwd'] = self.main_backup_server['ftp_password']
kwargs['ftp_root'] = self.main_backup_server['ftp_root']
return func(self, *args, **kwargs)
return wrapper()
но это неправильный код, в умных книгах примеры создания декоратора с передачей ему каких то параметров, а я хочу создать декоратор, который бы "копался" в параметрах оборачиваемой им функции и еще и дополнял их. А еще где его разместить, внутри класса или снаружи? Если снаружи, как ему self передать?
Буду признателен за любую мысль или ссылку на нее.
Ответы (2 шт):
В вашей задаче декораторы не нужны, но если вы просто изучаете декораторы...
Нужно понимать что декоратор - просто функция (на деле Callable, поэтому это может быть и __call__ у класса). Тонкость в том, что когда выполняется код модуля сверху вниз и встречается вызов декоратора @... то питон вызывает эту функцию, передает ей декорируемый объект и замещает декорируемый объект результатом работы этой функции.
То есть обычно это "принял класс/функцию и вернул класс/функцию", но на деле можно вернуть хоть 42
А значит нет никакой магии - функции ведут себя точно так же в плане передачи параметров и доступности self
В вашем коде вы и так "копаетесь в передаваемых параметрах и дополняете их", разве что вернуть должны wrapper (функция), а не wrapper() (результат вызова этой функции)
И если вам нужен декоратор внутри класса (свой self) - то это просто функция внутри класса. Меняется только способ вызова этого декоратора - вам нужен инстанс класса чтобы внутри декоратора был доступен self. При этом он не смешивается с self декорируемой функции, который передается внутри *args
class FtpParams:
def __init__(self, primary, second):
self.primary = primary
self.second = second
def set_ftp_vars(self, func):
def wrapper(*args, **kwargs):
if kwargs.get('second'):
params = self.second
del kwargs['second']
else:
params = self.primary
kwargs.update(**params)
return func(*args, **kwargs)
return wrapper
ftp = FtpParams(
{'host': 'primary.com', 'user': 'user'},
{'host': 'second.com', 'user': 'user'}
)
@ftp.set_ftp_vars
def foo(host, user):
print(f'{host}:{user}')
class Bar():
@ftp.set_ftp_vars
def bar(self, host, user):
print(f'{host}:{user}')
foo()
foo(second=True)
bar = Bar()
bar.bar()
bar.bar(second=True)
@vitidev Спасибо большое!!! я конечно изучаю декораторы, но всему свое место, и ваше
В вашей задаче декораторы не нужны
поставило все на место :-) "если какой то код повторяется в 3 местах просто вынеси его в отдельную функцию"
переписал
def _set_ftp_var(self, second):
if second:
ftp_params = {
'host': self.second_backup_server['name'],
'user': self.second_backup_server['ftp_user'],
'passwd': self.second_backup_server['ftp_password'],
'acct' : self.second_backup_server['ftp_user'],
}
ftp_root = self.second_backup_server['ftp_root']
else:
ftp_params = {
'host': self.main_backup_server['name'],
'user': self.main_backup_server['ftp_user'],
'passwd': self.main_backup_server['ftp_password'],
'acct' : self.main_backup_server['ftp_user'],
}
ftp_root = self.main_backup_server['ftp_root']
return (ftp_root, ftp_params)
second = False прописываю везде как именованный параметр методов (так и было раньше пока не задумал улучшать и стало красивее везде:
ftp_root, ftp_params = self._set_ftp_var(second)
with FTP(**ftp_params) as con:
con.cwd(ftp_root + segment)
........