What's the best way to define a function that depends on mutually excluding arguments, i.e. set of arguments where I only need to specify one at a time. A simple example would be a function that takes a physical parameter as the input, say the frequency. Now I want the user to be able to specify the frequency directly or the wavelength instead, so that they could equally call
func(freq=10)
func(wavelen=1).
One option would be kwargs, but is there a better way (regarding docstrings for example)?
Assuming all possible argument names are known, how about using a default of None?
def func(freq=None, wavelen=None):
if freq:
print(freq)
elif wavelen:
print(wavelen)
Using elif you can prioritize which argument is more important and considered first. You can also write code to return error if more than one argument is given, using xor:
def func(freq=None, wavelen=None):
if not(freq ^ wavelen):
raise Exception("More than one argument was passed")
if freq:
print(freq)
elif wavelen:
print(wavelen)
Since the calculations are going to be different, why not make that part of the function name and have two distinct functions (rather than a bunch of ifs):
def funcWavelen(w):
...
def funcFreq(f):
...
I wonder whether it is possible - and if so, how - to use an argument as a function parameter. I would like to be able to put in the parameters of my function, the 'ord' argument of numpy.linalg.norm(x, ord = ...)
I want my function to depend on a term which, depending on its value, changes the norm used. Thx
If you want to declare a function that evaluates the norm on an array, and allows you to pass in an order you can use something like this:
def norm_with_ord(x, order):
return numpy.linalg.norm(x, ord = order)
Though that still requires that you need to pass in one of the valid ordering values as listed here.
I've encountered a problem in a project where it may be useful to be able to pass a large number (in the tens, not the hundreds) of arguments to a single "Write once, use many times" function in Python. The issue is, I'm not really sure what the best ay is to handle a large block of functions like that - just pass them all in as a single dictionary and unpack that dictionary inside the function, or is there a more efficient/pythonic way of achieving the same effect.
Depending on exactly what you are doing, you can pass in arbitrary parameters to Python functions in one of two standard ways. The first is to pass them as a tuple (i.e. based on location in the function call). The second is to pass them as key-value pairs, stored in a map in the function definition. If you wanted to be able to differentiate the arguments using keys, you would call the function using arguments of the form key=value and retrieve them from a map parameter (declared with ** prefix) in the function definition. This parameter is normally called kwargs by convention. The other way to pass an arbitrary number of parameters is to pass them as a tuple. Python will wrap the arguments in a tuple automatically if you declare it with the * prefix. This parameter is usually called args by convention. You can of course use both of these in some combination along with other named arguments as desired.
Imagine I have a function that looks like this :
myFunction(arg, arg, kwarg, arg, arg, kwarg, etc...):
Where arg is an *arg and kwarg is a *kwarg. Before now, my function looked like myFunction(*args): and I was using just a long list of *args and I would just pass in a big list like this
myFunction(*bigList):
The bigList looked like = [[1,2,3],[4,5,6],'hello',[1,3,5],[2,4,6],'world',etc...]
But, now I need to have a kwarg every third argument. So, in my mind, the list "looks" like this now:
newBigList = [[1,2,3],[4,5,6],word='hello',[1,3,5],[2,4,6],word='world',etc...]
So, there are two questions to make this work.
1) Can I construct a list with a string for a kwarg without the function reading it in as an actual argument? Could the word(s) in the newBigList be strings?
2) Can you alternate kwargs and args? I know that kwargs are usually done with dictionaries. Is it even possible to use both by alternating?
As always, if anyone knows a better way of doing this, I would be happy to change the way I'm going about it.
EDIT Here's the method. Its a matplotlib method that plots a polygon (or a bunch of polygons):
plot([x1], [y1], color=(RBG tuple), [x2], [y2], color=(RGB tuple), etc...)
Where [x1] is a list of x values for the first polygon, [y1] is a list of y values for the first polygon, and so on.
The problem is, to use RBG values for the color argument, I need to include the color keyword. To further complicate matters, I am generating a random tuple using the random.random() module.
So, I have this list of lists of x values for all the polygons, a list of lists of y values for all my polygons, and a list of tuples of random RBG colors. They look something like this:
x = [[1,2,3], [4,5,6], [7,8,9]]
y = [[0,9,8], [7,6,5], [4,3,2]]
colors = [(.45, .645, .875), (.456, .651, .194), (.813, .712, .989)]
So, there are three polygons to plot. What I had been doing before I could do keywords was zip them all up into one tuple and use it like this.
list_of_tuples = zip(x, y, colors)
denormalized = [x for tup in list_of_tuples for x in tup]
plot.plot(*denormalized)
But, now I need those keywords. And I'm definitely happy to provide more information if needed. Thanks
The function signature doesn't work the way you think it does. Keyword arguments to matplotlib's plot function apply to all the lines you specify:
If you make multiple lines with one plot command, the kwargs apply to all those lines, e.g.:
plot(x1, y1, x2, y2, antialised=False)
If you want to specify individual colors to each line, you need to turn them into format strings that you can pass as every third positional argument. Perhaps you can format them as HTML style hex codes: #RRGGBB
Or alternately, call plot once per line and pass your color tuple just once as a keyword argument.
Short answer: No.
Longer answer: Depends on exactly what you are trying to do. The Python interface cannot accept the signature you want, so what is the function, and what are you actually trying to do?
There are several reasons that prevents you to do what you are trying to do:
You can specify a keyword only once in a function call, hence color=something, ..., color=other raises an exception
You cannot mix keyword arguments and positionals, so x1, y1, color=something, x2 is an error.
Even if this worked as you expected, there's still matplotlibs documentation that states:
If you make multiple lines with one plot command, the kwargs apply to
all those lines
I.e. you cannot use color= for only one of the lines, or once for each line. It's a "global" property. You have to use the other ways of providing line colors if you want to specify a different color for each line.
I believe, by your question, that you do not have clear how positional and keyword arguments work so I'll try to give you a clue in this regard.
First of all, there are different kind of parameters. I shall introduce an example to explain the differences:
def a_function(pos_kw1, pos_kw2, *args, kw_only)
This function has:
Two parameters pos_kw1, pos_kw2 which can be assigned both by a positional argument or a keyword argument
A parameter *args that can be specified only with positional arguments
A parameter kw_only that can be specified only with a keyword argument
Note: default values have nothing to do with being keyword parameters. They simply make the parameter not required.
To understand the mechanics of argument passing you can think as (although it's not strictly true) if when python performs a function call (e.g.):
a_function(1, 2, *'abc', kw_only=7)
It first collects all positional arguments into a tuple. In the case above the resultant tuple would be pos_args = (1, 2, 'a', 'b', 'c'), then collects all keyword arguments into a dict, in this case kw_args = {'kw_only': 7}, afterwards, it calls the function doing:
a_function(*pos_args, **kw_args)
Note: since dicts are not ordered the order of the keywords doesn't matter.
In your question you wanted to do something like:
plot(x, y, color=X, x2, y2, color=Y, ...)
Since the call is actually using *pos_args and **kw_args the function:
Doesn't know that color=X was specified right after y.
Doesn't know that color=Y was specified right after y2.
Doesn't know that color=X was specified before color=Y.
Corollary: you cannot specify the same argument more than once since python has no way to know which occurrence should be assigned to which parameter. Also when defining the function you simply couldn't use two parameters with the same name.
(And no, python does not automatically build a list of values or similar. It simply raises an error.)
You can also think that python first expands *pos_args without taking keyword arguments into account, and after that it expands **kw_args. If you think in this terms you can clearly understand that a function call such as:
# naive intent: assign pos_kw1 via keyword and pos_kw2 via positional
# assuming python will skip positional that were already provided as keyword args
a_function(1, pos_kw1=2)
# or even:
a_function(pos_kw1=2, 1) # hoping order matters
doesn't have any sense because the 1 is assigned to pos_kw1 via positional arguments, and when expanding the keyword arguments it would be reassigned.
Explained in an other way, in the call a_function(*pos_args, **kw_args) the *pos_args is a simple tuple-unpacking operation, equivalent to:
pos_kw1, pos_kw2, *args = pos_args
(in python2 you cannot use the *, but that's how the *args parameters work more or less).
Tuple-unpacking doesn't skip elements: it simply assign to consecutive elements of the tuple, and so do function calls: there is no check if a positional argument was already passed via keyword and eventually it's skipped. They are simply assigned, blindly.
Due to these restrictions it doesn't make any sense to allow function calls where positionals appear after keyword arguments hence you cannot do something like:
plot(x, y, color=X, x2, ...)
Allowing such function calls would only trick people making them think that order matters for keywords or that arguments could be skipped when unpacking etc. so Python simply raises an error and avoids this kind of ambiguity.
Is there a good rule of thumb as to when you should prefer varargs function signatures in your API over passing an iterable to a function? ("varargs" being short for "variadic" or "variable-number-of-arguments"; i.e. *args)
For example, os.path.join has a vararg signature:
os.path.join(first_component, *rest) -> str
Whereas min allows either:
min(iterable[, key=func]) -> val
min(a, b, c, ...[, key=func]) -> val
Whereas any/all only permit an iterable:
any(iterable) -> bool
Consider using varargs when you expect your users to specify the list of arguments as code at the callsite or having a single value is the common case. When you expect your users to get the arguments from somewhere else, don't use varargs. When in doubt, err on the side of not using varargs.
Using your examples, the most common usecase for os.path.join is to have a path prefix and append a filename/relative path onto it, so the call usually looks like os.path.join(prefix, some_file). On the other hand, any() is usually used to process a list of data, when you know all the elements you don't use any([a,b,c]), you use a or b or c.
My rule of thumb is to use it when you might often switch between passing one and multiple parameters. Instead of having two functions (some GUI code for example):
def enable_tab(tab_name)
def enable_tabs(tabs_list)
or even worse, having just one function
def enable_tabs(tabs_list)
and using it as enable_tabls(['tab1']), I tend to use just: def enable_tabs(*tabs). Although, seeing something like enable_tabs('tab1') looks kind of wrong (because of the plural), I prefer it over the alternatives.
You should use it when your parameter list is variable.
Yeah, I know the answer is kinda daft, but it's true. Maybe your question was a bit diffuse. :-)
Default arguments, like min() above is more useful when you either want to different behaviours (like min() above) or when you simply don't want to force the caller to send in all parameters.
The *arg is for when you have a variable list of arguments of the same type. Joining is a typical example. You can replace it with an argument that takes a list as well.
**kw is for when you have many arguments of different types, where each argument also is connected to a name. A typical example is when you want a generic function for handling form submission or similar.
They are completely different interfaces.
In one case, you have one parameter, in the other you have many.
any(1, 2, 3)
TypeError: any() takes exactly one argument (3 given)
os.path.join("1", "2", "3")
'1\\2\\3'
It really depends on what you want to emphasize: any works over a list (well, sort of), while os.path.join works over a set of strings.
Therefore, in the first case you request a list; in the second, you request directly the strings.
In other terms, the expressiveness of the interface should be the main guideline for choosing the way parameters should be passed.