More efficient/elegant branching
I agree with you, so I'm going to ignore def branch2(a, b, z): and def
branch3(a, b, z) because they appear too contrived - and I guess that's
fair, in that you've contrived to satisfy pylint.
Question: are there other people/factors who/which should be regarded as
more important than the linter's opinion?
The usual dictionary-controlled construct I've seen (and used) involves
using functions as the dict's values:
def branch1( *args): pass
alternative[ x ] = branch1
alternative[ choice ]( *args )
On 10/12/19 12:27 AM, Musbur wrote:
> 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
Is it ironic that the language does not have a form of case/switch
statement (despite many pleas and attempts to add it), yet the linter
objects to an if-elif-else nesting???
One reason why this code looks a bit strange (and possibly why PyLint
reacts?) is because it is trivial. When we look at the overall picture,
the question becomes: what will the 'mainline' do with "result" (the
returned value)? Immediately one suspects it will be fed into another
if result == "first": # do the first-thing...
if result == "second": # do the second-thing...
When found 'in real life', the above structure with such decisions is
almost certainly followed by a second decision to select which
'action(s)' will be carried-out. The original-code rarely stands-alone,
because all we've achieved is a labelling of the circumstances according
to the arguments provided.
Rather than using a variable for 'labelling', (all other things being
equal) I'd prefer to go straight to a function which 'does the business'
- and use the function's name as a documenting 'label'!
A quick reflection shows that I have vacillated between two approaches:
1 put the decision-making in the 'mainline' and call a variety of
functions/methods to do 'the first thing' etc. ie more-or-less what you
have here, except the cascade of decisions is not separated from 'what
def calculation( value, operator, operand):
if operator == "+":
add( value, operand)
elif operator == "-":
sub( value, operand)
2 arrange/call a series of functions each dealing with a single
circumstance, eg (borrowing from @Chris) should I "tap"?. Thus each
function will firstly check if the circumstances demand that action, ie
the applicable one of the if-clauses above, and if they do, then
implement that action: 'do the first thing' (etc).
value = handle_add( value, operator, operand )
value = handle_sub( value, operator, operand )
def handle_add( value, operator, operand):
if operator == "+":
return value + operand
Ugh - the example is no less contrived and grossly artificial...
The second of those appears somewhat 'cleaner' - in that we have 'hidden
away' all those pesky if-constructs; BUT it is NOT immediately obvious
exactly how many of the series of functions will actually result in any
'action' (if not, all of them). So, perhaps that raises the
(earlier/elsewhere) question of whether the various action-functions are
dependent or independent/exclusive of each other? It also requires
carefully choosing descriptive function names!