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"?
source to share
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])]
source to share
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.
source to share
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).
source to share
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
.
source to share