[Tutor] *args consumption

Smith smiles at worksmail.net
Sun Mar 12 09:58:00 CET 2006


Danny Yoo wrote:
|| 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?
|| 
|| ###
|| def foo(*arg):
||     pass
|| ###
|| 
|| automatically does this
|| 
|| ###
|| def foo(*arg):
||     if len(arg) == 1:
||         arg = arg[0]
||     pass
|| ###
| 
| 
| Yes: what if we're passing foo() some mixed data?  This might not be
| as weird as it might sound: imagine that a cartegian point [x, y] is
| represented either as a point:
| 
|    [x, y]
| 
| or as a single x coordinate number x where the y coordinate is
| assumed to be zero.
| 
|    x
| 
| Our list would then contain a mix of data.  For example, a list with
| the points (4, 3) and (5, 0) and (2, 9) could be represented as:
| 
|    [[4, 3], 5, [2, 9]]
| 
[cut]
| But if we pass playConnectTheDots() with a single point, we want the
| function not to automatically unpack the argument as if it were
| something else: it would be a lossy kind of implicit transformation.
|

OK, I think it's becoming clear. I should think of the *arg in the receiving function as containing a list of things that were sent to it (actually, a tuple). I am likely confusing things a bit because I want to be able to receive an x,y coordinate as foo(x,y) rather than something like foo((x,y)).

| If the case of len(points) == 1 is treated as a special case, that
| would make the logic turn into... well, I don't know, it would be
| ambiguous! 
|

As I understand it now, reducing it to a single point rather than leaving it as a tuple of length 1 destroys ones ability to iterate over the items that were passed in. e.g. if you call foo(42) and foo has a *args as its only argument, them automatcally changine the received value of (42, ) to 42 would destroy the ability to loop over args as 

###
for arg in args:
    #do something
###

As many times as I would prefer to have len == 1 arguments unpacked, if *args automatically behaved this way then likely the other half of the user-universe would be packing up things that have length 0 so they could iterate over them ;-)

###
if len(args) == 0:
    args = (args, )
###

| From the example above, if we did feed it:
| 
|    playConnectTheDots([[4, 3]])      ## Draw the point (4, 3)
| 
| vs:
| 
|    playConnectTheDots([4, 3])        ## Draw between (4, 0) and (3, 0)
| 
| then imagine what would happen if Python did the kind of automatic
| unwrapping you're thinking of.  How would it tell the difference
| between these two different cases?
| 
If playConnectTheDots looks like this,

###
def playConnectTheDots(*points):
    if len(points) == 1: points = points[0]
    for point in points:
        x,y = getX(point),getY(point)
        print 'plot %s %s' % (x,y)
###

then I imagine it will work exactly as you are describing :-)  I think you meant to call the function like this,

###
playConnectTheDots(*[[4, 3]])
playConnectTheDots(*[4, 3])
###

if the function is defined as this:

###
def playConnectTheDots(*points):
    for point in points:
        x,y = getX(point),getY(point)
        print 'plot %s %s' % (x,y)
###

How would you have written the first line(s) of playConnectTheDots? Or did you mean to pass the values like this:

###
playConnectTheDots(4, 3) #plot 4,0 and 3,0
playConnectTheDots([4, 3]) #plot 4,3
###

/c


More information about the Tutor mailing list