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 :-)
Интересно, насколько это медленнее, чем просто очередь условий и вызовов функций. Может руки дойдут, потестирую производительность.
Какие есть замечания, предложения?