#python #refactoring #instance-methods
#python #рефакторинг #методы экземпляра
Вопрос:
Я все еще относительно новичок в Python, 1-2 года обучаюсь самостоятельно и пытаюсь улучшить структуру своего кода, поэтому я рефакторингую некоторые старые программы, которые я написал. В одной программе я определил пару методов для записи файлов. Первый использует «write» для дампа огромного http-ответа. Второй использует «линии записи» для сброса различных производных списков, например списков ссылок, или форм, или других извлеченных данных.
Изначально я учитывал именование файла:
@property
def baseFilename(self):
unacceptable = re.compile(r'W ')
fname = re.sub(unacceptable,'-',self.myUrl)
t = datetime.datetime.now()
dstring = "%s%s%s%s%s%s" % (t.year, t.month, t.day, t.hour, t.minute, t.second)
fullname = fname '_' dstring '.html'
return fullname
Но у меня есть большой избыточный блок кода в каждом методе записи:
def writeFile(self, someHtml, writeMethod=write, prefix="RESPONSE_"):
'''The calling functions will supply only the data to be written and
static prefixes, e.g. "full_" for the entire http-response.
'''
fullpath = self.myDump prefix self.baseFilename
with open(fullpath, 'w') as h:
h.write(someHtml)
h.close()
print "saved %s" % fullpath
return fullpath
def writeList(self, someList, prefix="mechList_"):
'''Like write file but for one of the many lists outputted.
How do I refactor this, since redundant?
'''
fullpath = self.myDump prefix self.baseFilename
with open(fullpath, 'w') as h:
h.writelines(someList)
h.close()
print "saved %s" % fullpath
return fullpath
Я хотел бы иметь возможность добавлять переменную к каждой функции, которая определяет используемый метод записи, например (writeMethod=writelines). Я рассматривал возможность просто передать строку и использовать одну из функций черной магии — exec (), я полагаю, — но это не может быть правильным, поскольку, похоже, никто никогда не использует эти функции. Весь этот пример может быть относительно глупым, поскольку я мог бы просто обойти его, но я решил, что мне будет полезно знать, как передавать такого рода методы экземпляра (это правильный термин?). Связано ли это с привязкой и отменой привязки? Все, что мне нужно для хорошего ответа, — это синтаксис, необходимый для передачи ‘write’, ‘writelines’ и т.д. Может быть просто как: writeMethod = insert_your_syntax_here. Хотелось бы получить дополнительное объяснение или руководство. Спасибо.
Комментарии:
1. Это не «большой» избыточный блок кода. Он довольно маленький и сфокусированный. Я бы не стал с этим связываться. Все в порядке, как есть. Никто не выигрывает в code golf.
2. Кроме того,
h.close()
вызов является полностью избыточным — в конце концов, это то, что менеджер контекста делает за вас.3. Вы могли бы создать функцию для
fullpath
вычисления, возможно, это сэкономило бы вам «много» изменений позже. В остальном, я думаю, все выглядит нормально.4. @Skurmedel: Хорошее замечание о вычислении полного пути. Ценю совет.
5. @delnan: Я знал, что контекстные менеджеры действуют как средства защиты, но почему-то сложилось впечатление, что я все равно должен вызвать close. Спасибо за указатель.
Ответ №1:
Вы можете получить «связанный метод» из объекта, который затем можно вызывать как функцию без ссылки на объект.
f = obj.method
f(args)
# is equivalent to
obj.method(args)
Однако для вас это бесполезно, поскольку вы создаете объект, который хотите использовать только в методе — вы не можете передать его туда как связанный метод. Вы можете исключить создание fullpath
, хотя это экономит вам только половину избыточности. Одним из вариантов, который я бы счел излишним, была бы передача обратного вызова, который возвращает функцию для использования при записи.
Другим вариантом был бы декоратор, который выделял бы все общие части и передавал остальное в обратный вызов, оформленную функцию:
def uses_file(prefix_default):
def decorator(f):
@functools.wraps(f)
def decorated(self, data, prefix=prefix_default):
fullpath = obj.myDump prefix obj.baseFilename
with open(fullpath, 'w') as h:
f(h, data, prefix)
print "saved", % fullpath
return fullpath
return decorated
return decorator
# …
@uses_file(default_prefix="RESPONE_")
def writeFile(self, someHtml, prefix):
'''...'''
h.write(someHtml)
@uses_file(default_prefix="mechList_")
def writeList(self, someList, prefix):
'''...'''
h.writelines(someList)
Комментарии:
1. Хотя я беспокоюсь о сложности этого подхода, для любого, кто пытается понять, что я делаю, я ценю элегантность, и я могу легко представить ситуации, когда сложность была бы заслуженной. Действительно ценю, что вы нашли время дать такой подробный ответ. Это, безусловно, войдет в мою библиотеку декоратора. Еще раз спасибо.
Ответ №2:
Существуют различные способы сделать это, например, с помощью лямбд:
def writeFile(self, someHtml, writeMethod=lambda f, data: f.write(data),
prefix="RESPONSE_"):
'''The calling functions will supply only the data to be written and
static prefixes, e.g. "full_" for the entire http-response.
'''
fullpath = self.myDump prefix self.baseFilename
with open(fullpath, 'w') as h:
writeMethod(h, someHtml)
h.close()
print "saved %s" % fullpath
return fullpath