views:

471

answers:

4

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
+3  A: 

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.

ryidle
A: 

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.

Lennart Regebro
+7  A: 

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.

Ants Aasma
Predicting whether the caller will have the values in the local context (in the general case) is more difficult than it sounds! (I also figured that you should err on the side of an iterable.)
cdleary
A: 

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.

Roberto Liffredo