Smith wrote: > But there is another use that I am looking at right now (as encountered in the turtle module) where the arguments are analyzed in the function and not passed along. In such cases it seems like the first two lines of the function below are needed to get the passed arg out of the tuple form that is created and into the form expected in the function. > > ### > def goto(*arg): > if len(arg) == 1: > arg = arg[0] > x, y = arg > print x, y > goto(1,2) > pt = 1,2 > goto(pt) > ### > > Without those first two lines, passing 'pt' as in the example results > in a runtime error since arg = ((1, 2),) -- a tuple of length 1 -- cannot be unpacked into two items. > > MY QUESTION: since the pass_along function will work with this unpacking of length = 1 tuples and this is what you have to do anyway if you are going to consume the tuple in a function (as in the goto function above) is there a reason that this isn't automatically done for star arguments? Am I missing some other usage where you wouldn't want to unpack the *arg? If not, would the following "behind the scenes" behavior be possible/preferred?
Danny has given some reasons why this is not useful standard behaviour. If this is a behaviour you need for many functions, you could create a decorator that provides it so you don't have to include the same boilerplate in each function. Decorators are functions that accept a function as an argument and return a new function as a result. Here is a decorator that will unpack a tuple argument: In [7]: def accept_tuple_or_coordinates(f): ...: def unpacking_f(*args): ...: if len(args) == 1: ...: x, y = args[0] ...: else: ...: x, y = args ...: return f(x, y) ...: return unpacking_f ...: This shows the typical structure of a decorator. It defines a new function that wraps its argument with some new functionality, and returns the new function. Now to define a function of a point that can take either a single point or a pair of coordinates, just write a function of a pair of coordinates and decorate it: In [8]: @accept_tuple_or_coordinates ...: def goto(x, y): ...: print 'Going to', x, y ...: ...: By prefixing the function definition with @accept_tuple_or_coordinates the newly defined function is passed as the argument to the decorator, and the result is rebound to the name of the defined function. It is exactly as if you had written def goto(x, y): ... goto = accept_tuple_or_coordinates(goto) In fact if you want to use decorators in Python 2.3 you have to use this method. A decorator is reusable so you can have lots of functions like this: In [9]: @accept_tuple_or_coordinates ...: def draw(x, y): ...: print 'Drawing', x, y ...: ...: Try it out: In [10]: goto(1, 2) Going to 1 2 In [11]: p = (3, 4) In [12]: goto(p) Going to 3 4 In [13]: draw(p) Drawing 3 4 It works! There are some subtleties to decorators that you can ignore but you may not want to. For example with this simple example the function name is changed by the decorator: In [14]: draw Out[14]: <function unpacking_f at 0x0101B8B0> Docstrings are also lost. The solution is to copy these attributes in the decorator, like this: def accept_tuple_or_coordinates(f): def unpacking_f(*args): if len(args) == 1: x, y = args[0] else: x, y = args return f(x, y) unpacking_f.__name__ = f.__name__ unpacking_f.__dict__ = f.__dict__ unpacking_f.__doc__ = f.__doc__ return unpacking_f Now the function name and docstring is preserved: In [16]: @accept_tuple_or_coordinates ....: def draw(x, y): ....: ''' Draws a single point at the given coordinates ''' ....: print 'Drawing', x, y ....: ....: In [17]: draw Out[17]: <function draw at 0x0101BBF0> In [18]: draw.__doc__ Out[18]: ' Draws a single point at the given coordinates ' Kent _______________________________________________ Tutor maillist - Tutor@python.org http://mail.python.org/mailman/listinfo/tutor