0

I want to ask you your different points of view for the following scenario: imagine that we have several lists and something should be executed for those that are not empty:

if l1 or l2 or l3 or l4 or l5 or l6 or l7 or l8 or l9:

    print 'we have to do something in one or more lists'

    if l1:
        print 'l1'
        f1()

    if l2:
        print 'l2'
        f2()

    if l3:
        print 'l3'
        f3()

    if l4:
        print 'l4'
        f4()

    if l5:
        print 'l5'
        f5()

    if l6:
        print 'l6'
        f6()

    if l7:
        print 'l7'
        f7()

    if l8:
        print 'l8'
        f8()

    if l9:
        print 'l9'
        f9()

The code itself its is simple and understandable but this throws a (12) value for Mccabe complexity. To reduce this value, how would you approach it? I am very open an interested in hearing your thoughts.

Thank you in advance.

UPDATE:

Imagine exactly this concrete situation. How could you approach it?:

    if a:
        A = True

    if b:
        B = True

    if c:
        C = True

    if d:
        D = "D"

    if e:
        E = "E"

    if f:
        F = "F"

I think that, in this case, creating 6 different functions its not efficient and pythonic...

Solar
  • 445
  • 1
  • 4
  • 12
  • 1
    why even have the top `if`? The benefit of its short-circuiting in neglectable, and in the worst case (only `l9` is not empty) this code checks every list twice. – DeepSpace Oct 17 '18 at 08:05
  • Hi @DeepSpace, printing the name list it its exists its just an example, inside of it I've got to do different things, I'm gonna change it – Solar Oct 17 '18 at 08:08
  • This still doesn't answer the question why you need `if l1 or l2 or l3 or l4 or l5 or l6 or l7 or l8 or l9:` – DeepSpace Oct 17 '18 at 08:10
  • Possible duplicate of [How to avoid multiple flat if conditions in python?](https://stackoverflow.com/questions/41501833/how-to-avoid-multiple-flat-if-conditions-in-python) – Georgy Oct 17 '18 at 08:10
  • @DeepSpace I need to make something general if one of the list exist... – Solar Oct 17 '18 at 08:13
  • @Georgy not exactly, in that thread the same code must be executed if the condition is satisfied.. – Solar Oct 17 '18 at 08:14
  • 1
    @Solar Why the same? They have different values to append to the list. In the accepted answer it is proposed to iterate over conditions and those values. You could also iterate over conditions and the corresponding functions. Something like: `conditions = [l1, l2, ...]; functions = [f1, f2, ...]; for condition, function in zip(conditions, functions): if condition: function()` – Georgy Oct 17 '18 at 08:24
  • Or as mentioned there you could use `itertools.compress`: `for function in itertools.compress(functions, conditions): function()` – Georgy Oct 17 '18 at 08:35
  • @Georgy could be the approach! Thank you! – Solar Oct 17 '18 at 08:41

2 Answers2

1

Use a list of lists and a list of functions

def f1(l1):
   # do whatever is required to the  l1 list

def f2(l2):
   # similarly

# and similarly for f3 .. f9

...

lofl = [ l1, l2, l3, l4, l5, l6, l7, l8, l9 ]
loff = [ f1, f2, f3, f4, f5, f6, f7, f8, f9 ]

for f, l in zip( loff, lofl):
    if l:  # if the functions f cannot themselves safely do nothing for a falsy argument
        f( l) 

Hopefully, the number of functions required is somewhat smaller than nine (in this example). You could also easily pass the functions a parameter, so a function can be general and told what variant operation to perform at the time it is called

for f, l, p in zip( loff, lofl, lofp): # or, zip( loff, lofl, list(range(9)) )
    f(l, p)
    

or indeed, even pass the functions an arbitrary set of keyword arguments

lofargs=[ { 'foo':1, 'bar':'Monty' }, # kwargs for f1
          { 'foo':2, 'bar':'Python'}, 
          { 'foo':3 }, 
          {},                     # no kwargs at all for f4, 
          ...
        ]


for f, l, k in zip( loff, lofl, lofargs):
    f( l, **k )

Revisiting this a few years later, I'd now go all-in on the directory approach:

callstruct = [
          { 'func':f1, 'arg':l1, 'foo':1, 'bar':'Monty' },
          { 'func':f2, 'arg':l2, 'foo':2, 'bar':'Python'}, 
          { 'func':f3, 'arg':l3, 'foo':3 }, 
          { 'func':f4, 'arg':l4},                     # no kwargs at all for f4, 
          ... 
          ]

for d in callstruct:
    func = d.pop('func')
    arg = d.pop('arg')
    func( arg, **d)

If there's any outer loop, be aware that .pop will mutate the dicts in callstruct, so you may want to start with dd = d.copy() and work on dd.

Not sure why I now feel this way. Maybe too much JavaScript exposure? I've been finding one-liners like this in my recent code quite often. Much more readable and extensible than lots of ifs

 x = {'foo':1, 'bar':2, 'baz':3}.get(choice, DEFAULT) # or ERROR, or catch KeyError
nigel222
  • 7,582
  • 1
  • 14
  • 22
0

If you put all the lists in a list of lists:

lol = [l1,l2,l3,l4,l5,l6,l7,l8,l9]

Then you can iterate over the list of lists:

for l in lol:
    if l:
        # do something
T Burgis
  • 1,395
  • 7
  • 9
  • @T Burgis Could be a valid approach, but the things is that I cannot execute the same code for all lists. So in that case I'd have to make more conditions. – Solar Oct 17 '18 at 08:11
  • You need a more complicated data structure to hold the lists then. Perhaps a dictionary - the key tells you what to do to the list, the value is a list of lists? – T Burgis Oct 17 '18 at 08:13