3

I was just wondering how would a pythonic code look like to tackle this problem:

Suppose you have a function:

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

And inside that function you want to construct corresponding 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 more beautiful ways of writing this code? If not maybe optional parameters is not "the way to go"?

6
  • 1
    Is there a reason that you don't want the user to pass in the elementslist? Commented Jul 9, 2017 at 20:41
  • Definitely possible. Although its just a philosophical and aesthetic problem. Other approaches are welcome! Commented Jul 9, 2017 at 20:46
  • 1
    @LaimonasSutkus Question is, what is A, B, C, and D? Commented Jul 9, 2017 at 20:48
  • Sorry for not being specific enough. But the intention was that i construct new objects here. A B C and D are classes. But would it change something if I was about to say A B C and D are methods ? Commented Jul 9, 2017 at 20:50
  • It seems a bit unusual for these separate arguments to control the presence of elements of the same list. If these elements are homogeneous enough that they're handled in the same list, I would expect a more unified form of input than as separate arguments; alternatively, with A, B, C, and D all being separate classes, it may not make sense to stuff all the objects in the same list. Commented Jul 9, 2017 at 21:08

5 Answers 5

2

Are there any more beautiful ways of writing this code?

To be honest, in my opinion no. You could make a clever hack and use that, but the purpose of writing good code is to make it clear and readable, and easily understandable. All of which I don't think any other method would qualify as being, except what you have now.

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

However, this also depends on how you pass your arguments to your function. What you're doing now seems a bit strange. It would probably be much more natural to pass in a list, or a dictionary instead.

Sign up to request clarification or add additional context in comments.

Comments

1

You could 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 Comment

@juanpa.arrivillaga You're right. There are better approaches to solve that. I updated the answer :)
1

How about this?

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

Your whole approach looks rather strange, though. Probably you shouldn't have those arguments like that. But without knowing more about what you actually need...

Comments

0

You have a number of objects that need to call according to a condition, the repetition of the if statements 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]

Irrespective of what those callable objects do now, you just call them and whatever they return is collected and held inside the list returned by do_stuff. Future expansion is easier now, just remove the unwanted objects from objs dictionary or set their keys to False or really to any object whose boolean value is False.

Comments

0

Here's a fairly clean way (and extensible) way of doing it:

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 and 1 in place of False and True because Python will treat them the same (it considers the "truthiness" of non-boolean values by whether or not they're 0 or empty containers, depending on their type).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.