Basically I am writing a function that depends on a numerical input x, a number between 0 and 1. I want the default value of x to be, say, x=0.5. However, I also want to provide an option to the user that allows them to let the program select x for them using some algorithm. Is there an elegant way to handle that choice with one function argument?
I'm thinking something like this:
def foo(x=0.5):
if x == "pick for me":
return complicated_algorithm_that_picks_x()
else:
return x
def complicated_algorithm_that_picks_x():
print "Thinking hard..."
return 0.1234567
which would return:
>>> foo()
0.5
>>> foo(0.3)
0.3
>>> foo("pick for me")
Thinking hard...
0.1234567
But this looks really inelegant, since the user has to know what magic string to pass to invoke the selection algorithm. Any ideas how I can handle this more cleanly?
I was thinking having an additional Boolean argument called pick (that defaults to False), which when True will invoke the x picking function. But then users might pass both, say, x=0.3 and pass=True, in which case I have to arbitrarily ignore one of the choices. Looks clumsy again.
There are three things you might consider:
Here there are, in no particular order:
If you want to do two different things in one function and you're having trouble designing a natural interface, it might be a sign that the one function should become two:
def foo_picked_for_me():
x = pick_x()
return foo(x)
def foo(x):
# do foo
pass
I don't know how this strikes you, but it's simple, clear, and that means its often preferable.
Default arguments are nice, but a function's interface can only get so complicated before it starts making more sense to handle option setting with a class:
class Foo:
def __init__(self):
self.x = 0.5
def pick_x_for_me(self):
self.x = pick_x()
def foo(self):
# do foo with self.x
As EOL suggests below, it's perfectly pythonic to leave x
"exposed", and to allow the user to change it. You say, though, that x
must be between 0 and 1, so it might make sense to do some bounds checking with the setter for x
:
class Foo(object):
def __init__(self):
self._x = 0.5
@property
def x(self):
return self._x
@x.setter
def x(self, value):
if 0 <= value <= 1:
self._x = value
else:
raise ValueError("x must be between 0 and 1")
def pick_x_for_me(self):
self._x = pick_x()
def foo(self):
pass
# do foo with self._x
The last option is analogous to what other posters have given: use two arguments, and throw an exception if the user does something contradictory. I'd consider allowing three forms of call:
# x gets its default value of 0.5
foo()
# x gets the specified value
foo(x=.42)
# x is picked for me
foo(pick_for_me=True)
Additionally, if I write:
foo(x=.42, pick_for_me=True)
I'll throw an exception. Some code that implements this follows:
def foo(x=None, pick_for_me=None):
if x is None and pick_for_me is None:
x = 0.5
elif pick_for_me and x:
raise RuntimeError("You can't set both!")
elif pick_for_me:
x = picking_algorithm()
# else x was set, so leave it be
This is kind of complicated, and I'm not so sure I like the API. Just make sure you document the behavior well enough so that the user knows how to use the thing.
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments