Хороший интерфейс для восстановления эффекта функции

В моей работе мы часто пишем функции, которые производят вычисления и записывают результат в базу данных. Иногда допускаются ошибки, и в конечном итоге сохраняется неправильный материал, поэтому мы также хотим предоставить интерфейс для очистки после функции. Это попытка решить эту проблему.

from functools import wraps

def revert(func):
    if not hasattr(func,'_revert'):
        raise NotImplementedError(func.__name__ + " does not have a revert registered")

    @wraps(func._revert)
    def wrapper(*args,**kwargs):
        return func._revert(*args,**kwargs)
    return wrapper

def register_revert(revert):
    def revert_added(func):
        func._revert = revert

        @wraps(func)
        def wrapper(*args,**kwargs):
            return func(*args,**kwargs)
        return wrapper
    return revert_added

def _is_it_a_str(maybe_str):
    if not isinstance(maybe_str,str):
         raise ValueError("I need a string")

def _revert_stuff(a_str):
    _is_it_a_str(a_str)
    print('revert stuff with ' + a_str)

@register_revert(_revert_stuff)
def do_stuff(a_str):
    _is_it_a_str(a_str)
    print('doing stuff with ' + a_str)


do_stuff('something')
# Oops:
revert(do_stuff)('something')

Идея состоит в том, чтобы написать вашу функцию, выполняющую вашу работу — в данном случае do_something. А затем, когда вы будете готовы перейти на следующий уровень, вы напишете его откат, зарегистрируете, и все готово.

Мне нравится, что

  • решение не требует ничего менять в основном функционале.
  • вы можете использовать один и тот же идентичный интерфейс для многих функций
  • синтаксис читается относительно интуитивно

Я считаю неудобным то, что

  • Мне нужно поддерживать два идентичных интерфейса — один на do_stuff и _revert_stuff должен быть идентичным (или, по крайней мере, _revert_stuff должен работать, получая тот из do_stuff).
  • иногда я хочу перепроверить аргументы, которые я затем должен повторить (пример здесь с _is_it_a_str). Я думал о том, чтобы передать это другому декоратору, но, боюсь, это слишком запутывает.

Изменить: мы используем новейшую стабильную версию Python

ps Мой первый вопрос старался изо всех сил. Критика приветствуется!

1 ответ
1

def revert(func):
    @wraps(func._revert)
    def wrapper(*args,**kwargs):
        return func._revert(*args,**kwargs)
    return wrapper

Вы возвращаете оболочку, которая принимает аргументы, вызывает функцию и возвращает результаты …

Я почти уверен, что это просто длинный способ написать:

def revert(func):
    return func._revert

Возникает вопрос: зачем хранить функцию возврата как func._revert, когда вы могли сохранить его как func.revert? Тогда вместо:

revert(do_stuff)('something')

можно было написать:

do_stuff.revert('something')

что на один символ короче. Еще лучше: автозаполнение может сказать вам, есть ли .revert член для do_stuff, что невозможно с помощью revert(...).

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *