0

So let's say I have a function which takes two params, but at least one of them should be present:

 def foo_bar(foo = None, bar = None):

The problem is that both of them are optional, but in practice either foo or bar will be passed in. Right now I check for existence in the function like this:

do_this() if foo else do_that() sort of logic.. 

But I don't like the way this looks.

What's the best way to deal with this?

clwen
  • 20,004
  • 31
  • 77
  • 94
frazman
  • 32,081
  • 75
  • 184
  • 269

4 Answers4

4

I think you've got it right. The params can also be read with the kwargs syntax:

def foo(*args, **kwargs):
    if 'foo' in args:
        do_this()
    elif 'bar' in args:
        do_that()
    else:
        raise ValueError()

OR, you could do something like this:

def foo(param, flag):
    if flag == 'foo':
        do_this()
    elif flag == 'bar':
        do_that()
    else:
        raise ValueError()

Either way should be fine. Hope this helps

inspectorG4dget
  • 110,290
  • 27
  • 149
  • 241
  • not sure what's better about the **kwargs version compared to the original... I like the param/flag version though. – weronika Aug 28 '12 at 23:14
  • It works as flags like you said. It also works in allowing params to be passed in any (essentially orderless) order. The key/value pairs of the `dict` become how params are passed (and parsed), as opposed to the positional passing of parameters which is what would happen if the function signature was `def foo(param1, param2, …)`. Also, check out the answers to [this other SO question](http://stackoverflow.com/questions/3394835/args-and-kwargs) – inspectorG4dget Aug 29 '12 at 02:40
  • I know how that works, but with a signature like `def foo(x,y,z)` you can *also* pass the parameters in any order by name (`foo(z=1, x=2, y=3)`), which is why I'm not sure there's any advantage to the `**kwargs` signature in this case. – weronika Sep 07 '12 at 00:00
2

3 ways to deal with it come to mind:

If you can switch behavior based upon some rules about the parameters (one is of one type, one is of another, one matches 1 regex, 1 another, etc), pass one parameter and figure out what to do inside the function.

Create 2 functions that both call some other function for their shared behavior but handle the foo and bar specific cases independently.

Pass 1 data value and another control value:

def foo_bar(val, control):
    if control=='foo':
        #Do foo stuff
    elif control=='bar':
        #Do bar stuff
Silas Ray
  • 25,682
  • 5
  • 48
  • 63
2

You are on the right track, but What if both are specified? What I usually do is:

def some_func(foo=None, bar=None):
    "either one, or neither, but not both, are allowed"
    if foo is not None and bar is not None:
        raise TypeError("cannot specify both 'foo' and 'bar'")
    if foo is not None:
        pram = foo
    elif bar is not None:
        pram = bar
    else:
        pram = None  # or whatever

Simple, easy to understand.

Ethan Furman
  • 63,992
  • 20
  • 159
  • 237
0

Why not simply check to see which one is None, and say that the other one must be the one that is present?

if foo == None:
    do bar()
elif bar == None:
    do foo()
else:
    throwUpYourHandsInFrustration()
algorowara
  • 1,700
  • 1
  • 15
  • 15