More efficient/elegant branching
Peter Otten
__peter__ at web.de
Mon Dec 9 08:49:13 EST 2019
Musbur wrote:
> Hello,
>
> I have a function with a long if/elif chain that sets a couple of
> variables according to a bunch of test expressions, similar to function
> branch1() below. I never liked that approach much because it is clumsy
> and repetetive, and pylint thinks so as well. I've come up with two
> alternatives which I believe are less efficient due to the reasons given
> in the respective docstrings. Does anybody have a better idea?
>
> def branch1(a, b, z):
> """Inelegant, unwieldy, and pylint complains
> about too many branches"""
> if a > 4 and b == 0:
> result = "first"
> elif len(z) < 2:
> result = "second"
> elif b + a == 10:
> result = "third"
> return result
I agree with Chris that this is the way to go.
pylint be damned ;)
>
> def branch2(a, b, z):
> """Elegant but inefficient because all expressions
> are pre-computed althogh the first one is most likely
> to hit"""
Also, it doesn't work in the general case, like
decision = [
(a == 0, "first"),
(b/a > 1, "second"),
...
> decision = [
> (a > 4 and b == 0, "first"),
> (len(z) < 2, "second"),
> (b + a == 10, "third")]
> for (test, result) in decision:
> if test: return result
>
> def branch3(a, b, z):
> """Elegant but inefficient because expressions
> need to be parsed each time"""
> decision = [
> ("a > 4 and b == 0", "first"),
> ("len(z) < 2", "second"),
> ("b + a == 10", "third")]
> for (test, result) in decision:
> if eval(test): return result
You can shave off most of the overhead by precompiling the expressions:
DECISIONS = [
("a > 4 and b == 0", "first"),
("len(z) < 2", "second"),
("b + a == 10", "third")
]
DECISIONS4 = [
(compile(expr, "<nofile>", "eval"), result)
for expr, result in DECISIONS
]
def branch4(a, b, z):
for test, result in DECISIONS4:
if eval(test):
return result
raise ValueError
Using lambdas instead of precompiled expressions is a tad faster:
DECISIONS5 = [
(eval("lambda a, b, z: " + expr), result)
for expr, result in DECISIONS
]
def branch5(a, b, z):
for test, result in DECISIONS5:
if test(a, b, z):
return result
raise ValueError
This is is a slippery slope as you might now consider building branch1()
from the DECISIONS list...
More information about the Python-list
mailing list