osdir.com


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

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

and thus:

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 
decision, eg:

	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 
came before'.

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 )

where:

def handle_add( value, operator, operand):
	if operator == "+":
		return value + operand
	return value

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!
-- 
Regards =dn