Pattern Matching Guards in Python
def pattern_match(n, s="__fns"):
s=n+s;g=globals()
def m(f):
def a(*r):
for f in g[s]:
if reduce(lambda c,t:c and eval(t[1:],{},dict(zip(f.func_code.co_varnames,r))),filter(lambda x:x and x[0]is"|",map(lambda x:x.strip(),f.func_doc.split("\n")))): return f(*r)
g[n]=a;g[s]=(g.get(s)or[])+[f]
return a
return m
The body of the function comes in at 288 characters.
Pattern Matching Guards allow you to use completely different functions depending on argument values. Although it can be easily emulated with a series of if
statements, pattern matching guards can help separate sections of code, and it's a great excuse to do some crazy metaprogramming.
pattern_match
is a decorator that creates a new function that implements pattern matching guards.
The conditions for each "sub-function" given in each docstring on lines starting with a pipe (|
). If all conditions evaluate truthily, that version of the function is run. Functions are tested in order until a match is found. Otherwise, None
is returned.
An example will help clarify:
@pattern_match("test1")
def test1_a(a, b, c):
"""
This <s>pattern</s> guard tests if a and c are positive
| a > 0
| c > 0
"""
return a + b + c
@pattern_match("test1")
def test1_b(a, b, c):
"""
This pattern only ensures b is positive
| b > 0
"""
return b + c
@pattern_match("test1")
def test1_c(a, b, c):
"""
Final catchall
| True
"""
return 0
print test1(1,2,3) # (a) >>> 6
print test1(1,2,0) # (b) >>> 2
print test1(1,0,0) # (c) >>> 0
print test1(0,0,1) # (b) >>> 1