A pythonic way to construct an array based on boolean arguments?

I'm just wondering what the pythonic code looks like to solve this problem:

Suppose you have a function:

def do_stuff(a=True, b=True, c=True, d=True):

      

And inside this function, you want to build the relevant objects:

elements = []
if a:
    elements.append(A())
if b:
    elements.append(B())
if c:
    elements.append(C())
if d:
    elements.append(D())

      

Are there any nicer ways to write this code? If not, perhaps the optional parameters are not "way to go"?

+3


source to share


5 answers


How about this?

elements = [X() for x, X in ((a, A), (b, B), (c, C), (d, D)) if x]

      



Your whole approach looks pretty strange. You probably shouldn't have such arguments. But not knowing more about what you really need ...

+1


source


You can use itertools.compress

:



from itertools import compress

def do_stuff(a=True, b=True, c=True, d=True):
    elements = [val() for val in compress([A, B, C, D], [a,b,c,d])]

      

+1


source


Are there any nicer ways to write this code?

To be honest, in my opinion no . You can make a smart hack and use it, but the goal of writing good code is to make it clear and understandable and easy to understand. All I do not think is that any other method will qualify as a creature other than what you have now.

Of course, this looks quite verbose, but it is better to have verbosity rather than unnecessary complexity. Sometimes the obvious way is the most idiomatic, reliable, clean, and "pythonic" solution. In this case, I think it's best to do KISS.

However, it also depends on how you are passing your arguments to your function. What you are doing now seems a little strange. It would probably be much more natural to jump into a list or dictionary.

+1


source


Here's a pretty clean way (and extensible):

class A: pass
class B: pass
class C: pass
class D: pass

def do_stuff(a=True, b=True, c=True, d=True):
    classes, args = (A, B, C, D), (a, b, c, d)
    elements = [classes[i]() for (i, arg) in enumerate(args) if arg]
    # Show results.
    print(list('class {} obj'.format(element.__class__.__name__) for element in elements))

do_stuff(1,1,1,1)  # -> ['class A obj', 'class B obj', 'class C obj', 'class D obj']
do_stuff(0,1,1,0)  # -> ['class B obj', 'class C obj']
do_stuff()         # -> ['class A obj', 'class B obj', 'class C obj', 'class D obj']
do_stuff(0,0)      # -> ['class C obj', 'class D obj']
do_stuff(b=False)  # -> ['class A obj', 'class C obj', 'class D obj']

      

Note. I use 0

both 1

instead of False

and True

because Python will treat them the same (it counts the "truthfulness" of non-booleans as whether they are 0

or empty containers, depending on their type).

0


source


You have multiple objects that need to be called according to a condition, repetition of statements if

is something to avoid:

objs = {True:A, True:B, True:C ,...}

def do_stuff(objs):
    return [obj() for key, obj in objs.items() if key]

      

Regardless of what these callables do, you simply call them and whatever they return is collected and held inside the list they return do_stuff

. The future extension is now easier, just remove unnecessary objects from the dictionary objs

or set their keys to False

or indeed to any object whose boolean value False

.

0


source







All Articles