Blog Trac Lyrics for music

Archive for September 17, 2007

Guards в Python

В момент чтения книги по Эрлангу мне пришла в голову мысль - а почему бы не сделать guard’ы в Питоне? Инструмент, который можно для этого приспособить, существует - это декораторы.

Недолго думая, я написал первый вариант, потом ещё раза три его переделал, и вот что получилось в итоге:

def _main_guard(main_func):
    def inner(*args, **kwargs):
        for guard, func in inner.guards:
            if guard(*args, **kwargs):
                return func(*args, **kwargs)
        return main_func(*args, **kwargs)

    inner.guards = []
    return inner

def guard(guard_clause=None):
    if not guard_clause:
        return _main_guard

    def decor(func):
        main_func = func.func_globals[func.__name__]
        main_func.guards.append((guard_clause, func))
        return main_func
    return decor

Вот пример использования:

>>> @guard()
... def x0(x, y, z=5):
...     print "Standard: x = %d, y = %d, z = %d" % (x, y, z)
...
>>> @guard(lambda x, y, z=0: x == 1)
... def x0(x, y, z=87):
...     print "First: x = %d, y = %d, z = %d" % (x, y, z)
...
>>> @guard(lambda x, y: x == 2 and y == 4)
... def x0(x, y, z=7):
...     print "Second: x = %d, y = %d, z = %d" % (x, y, z)
...
>>> x0(0, 1)
Standard: x = 0, y = 1, z = 5
>>> x0(1, y=2)
First: x = 1, y = 2, z = 87
>>> x0(x=2, y=4)
Second: x = 2, y = 4, z = 7
>>> x0(2, 21)
Standard: x = 2, y = 21, z = 5

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

Сейчас в планах сделать так, чтоб можно было писать вот так:

@guard(lambda: x == 2 and y == 4)
def x0(x, y, z=7):
    print "First: x = %d, y = %d, z = %d" % (x, y, z)

а guard сам будет подставлять аргументы в проверочную лямбду соответственно тому, как они написаны в самой функции - так будет лучше выглядеть, меньше ошибок и вообще хардкорнее. Эксперименты показали, что такое однозначно возможно и, в принципе, не так уж и сложно сделать. К сожалению, без слова lambda сделать ну никак не получится - всё-таки это Python, а не Lisp :-)

Интересно, насколько это медленнее, чем просто очередь условий и вызовов функций. Может руки дойдут, потестирую производительность.

Какие есть замечания, предложения?