October 31, 2003
Tuple arguments

I've just come across this while browsing through Python-Dev - tuple arguments. WTF?

So, you can do:

>>> def f(a, (b, c), d):
... print a, b, c, d
...
>>> f('a', ('b', 'c'), 'd')
a b c d

What I can't work out is - why?

Posted to Python by Simon Brunning at October 31, 2003 05:27 PM
Comments

Isn't it just a consequence of the fact that you can always expand out a tuple?

(a,b,c) = f_that_returns_3ple()

Posted by: Adam Vandenberg on October 31, 2003 06:07 PM

Serves you right for reading python-dev I say ;-)

I'd suspect its because you bind the elements of the tuple to specific identifiers in the function definition. If you had done;

def f(a, b, d):
print a, b[0], b[1], d

f('a', ('b', 'c'), 'd')

you would have got the same result. Probably

Posted by: Andy Todd on October 31, 2003 08:46 PM

I think it comes from ML. In ML the expression "x y" means "call x with y" for any x and y. "x(y, z)" means "call x with (y, z)", where (y, z) is a tuple like it is in Python. So tuple unpacking as part of a function signature is a core concept in ML. I believe Python took inspiration from ML in terms of tuples and their unpacking (or maybe Python and ML were both inspired by some other preceding language).

But in Python any two adjacent expressions don't imply that the first is called with the second as an argument, instead the parenthesis are explicitly required. And while some arguments can be considered a tuple, there's no data structure to hold both positional and keyword arguments, nor a way to unpack those arguments (outside of the Python internals). So Python functions aren't syntactically that close to ML functions, but have this weird vestigal feature that might have seemed useful or natural or symmetrical at one time.

Posted by: Ian Bicking on October 31, 2003 08:47 PM

It seems a natural thing to me - argument passing is just a form of assignment, and assignment supports tuple packing and unpacking. It is also a useful check on your arguments sometimes - for example:

def draw_rect((x1, y1), (x2, y2)): ...

It is clear to the reader what the function is expecting, and the compiler will check it for you as well. I don't use this all that often, but I wouldn't like to see it disappear

Posted by: Mark Russell on November 1, 2003 12:04 PM

Look at PersistentPairKeysDict of the SchoolTool project.

http://source.schooltool.org/svn/trunk/schooltool/src/schooltool/db.py

We wanted a dict-like API that takes a key composed of a ZODB persistent object and some hashable object. The pythonic way to do this is to use the same protocol as dict, but make the key a two-tuple. So, __getitem__ looks like this::

def __getitem__(self, (persistent, hashable)):
return self._data[persistent][hashable]

(I hope the comments formatting doesn't mangle those two lines of code.)

Posted by: Steve Alexander on November 5, 2003 03:00 PM

Buggered them! But don't worry, I'm sure that we can reconstruct them.

Thing is, Steve, that only saves one line of code over:

def __getitem__(self, key):
persistent, hashable = key
return self._data[persistent][hashable]

One line is all that you'd *ever* save, and I don't think that the shorter version is any more readable either.

Me, I think that it's either something historical, as Ian suggests, or it's entirely accidental - a side effect of some feature which *was* wanted.

Posted by: Simon Brunning on November 5, 2003 03:49 PM
Post a comment
Name:


Email Address:


URL:



Comments:


Remember info?