args v. *args passed to: os.path.join()

Discussion in 'Python' started by Pierre Fortin, Sep 18, 2004.

  1. This quest for understanding started very innocently...

    A simple error on my part, passing on args as "args" instead of "*args" to
    os.path.join() led me to wonder why an error wasn't raised...

    def foo(*args):
    ...
    return os.path.join(*args)

    foo('a','b') # returns 'a/b'

    With: return os.path.join(args)

    foo('a','b') # returns ('a', 'b') (unchanged -- no error)

    Replacing the 'return's in the function with each print below in turn
    produced the results in the comments:

    # without '*'
    print args # ('a', 'b')
    print (args) # ('a', 'b')
    print "/".join(args) # a/b
    print string.join(args,"/") # a/b
    print os.path.join(args) # ('a', 'b')

    # with '*'
    print *args # syntax error at *
    print (*args) # syntax error at *
    print "/".join(*args) # TypeError
    print string.join(*args,"/") # syntax error at 2nd (")
    print os.path.join(*args) # a/b

    The results in matrix form (numbers refer to order of print statements):

    args *args
    1 ('a', 'b') syntax error
    2 ('a', 'b') syntax error
    3 a/b TypeError
    4 a/b syntax error
    5 ('a', 'b') a/b

    os.path.join() is the only one in this list which doesn't return an error.
    Also, os.path.join()'s results are reversed relative to the others by
    requiring *args...

    My question is: other than discovering this anomaly (and wrong result) at
    runtime, is there a simple rule for when to pass on '*args' v. 'args' that
    I've somehow missed...?

    Thanks,
    Pierre
    Pierre Fortin, Sep 18, 2004
    #1
    1. Advertising

  2. Before applying args or *args to a real world sample, lets see what each of
    them does:

    >>> def testfunc(*args):

    .... print args
    ....

    Define a function called testfunc, for which all positional arguments are
    stored to the tuple args. The body of this functions contains a single
    statement which prints this tuple to screen.

    >>> testfunc([1,2,3])

    ([1, 2, 3],)

    The tuple which is printed to screen is the following: a tuple with a single
    item which is a list which contains the elements 1, 2 and 3 in this order.

    >>> testfunc(*[1,2,3])

    (1, 2, 3)

    The tuple which is printed to screen is the following: a tuple with the items
    1, 2 and 3 in this order.

    See where we're heading at? Let's see another example.

    >>> def testfunc2(a,b):

    .... print a
    .... print b
    ....

    Lets define another function which takes two arguments (a, b) and prints them
    out in order.

    >>> testfunc2([1,2])

    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    TypeError: testfunc2() takes exactly 2 arguments (1 given)

    Lets call this function with a single argument (a list, which contains the two
    items). The interpreter duly complains that the function takes two arguments,
    but only one is given, and raises a TypeError.

    >>> testfunc2(*[1,2])

    1
    2

    Again, using the star syntax: We pass two arguments (because the list which is
    starred contains two arguments), and these end up in a and b, respectively.

    So, what does the star-operator do? It splits up an iterable (a list in this
    case) and fills the first len(iterable) positional arguments with the values
    it got from the list.

    This is why you see different behavior when passing a star or not. Let's look
    at the documentation for os.path.join:

    join(a, *p)
    Join two or more pathname components, inserting '/' as needed

    This means that you have to pass at least one argument (a), which is the path
    base, and may pass more arguments (*p) which are joined with this item,
    inserting slashes as needed.

    Now, when you call:

    os.path.join(["a","b"])

    the list ends up in the a parameter, and because the function doesn't have to
    do anything (there are no more arguments), the list is returned unchanged
    (although this should probably raise a TypeError, anyone?).

    But when you call:

    os.path.join(*["a","b"])

    the first item of the list ends up as parameter a to the function, the second
    item of the list ends up as the second parameter to the function, which are
    then duly joined by the function.

    And one more look why "".join(*["a","b"]) raises an error:

    join(...)
    S.join(sequence) -> string

    Return a string which is the concatenation of the strings in the
    sequence. The separator between elements is S.

    The documentation for str.join() states that you pass it one parameter, a
    sequence.

    Now, when you do "".join(*["a","b"]), the number of passed parameters is two,
    and because the function only accepts one parameter, a sequence, the function
    call raises an Exception.

    HTH!

    Heiko.
    Heiko Wundram, Sep 18, 2004
    #2
    1. Advertising

  3. On Sat, 18 Sep 2004 20:08:35 +0200 Heiko wrote:

    > Now, when you call:
    >
    > os.path.join(["a","b"])
    >
    > the list ends up in the a parameter, and because the function doesn't
    > have to do anything (there are no more arguments), the list is returned
    > unchanged (although this should probably raise a TypeError, anyone?).

    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ =======

    That was the whole point; the rest was pretty much my understanding too...
    I can't think of any reason why os.path.join() should ever be presented
    anything but strings... on the other hand, maybe it could
    check for lists/tuples and use those to return the expected "a/b"...

    Then again, I just checked a number of functions in os and os.path;
    os.path.join() seems to be the only one which fails to return a
    TypeError...
    Pierre Fortin, Sep 18, 2004
    #3
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. dee
    Replies:
    9
    Views:
    495
    Joseph Byrns
    Apr 15, 2005
  2. Alan Silver
    Replies:
    0
    Views:
    870
    Alan Silver
    Jun 5, 2006
  3. Anand
    Replies:
    2
    Views:
    890
    Anand
    Sep 11, 2003
  4. Replies:
    3
    Views:
    481
    David Eppstein
    Sep 17, 2003
  5. Steve
    Replies:
    1
    Views:
    945
    Fredrik Lundh
    Dec 13, 2005
Loading...

Share This Page