Python Isn't Perfect: adding a 'gotchas' section to the tutorial

Although I love Python there are some aspects of the language design which are disappointing and which can even lead to problems in some cases. A classic example is a mutable default argument having the potential to produce unexpected side-effects, as a consequence of the non-intuitive scoping rules. Another awkward 'feature' is the requirement for a trailing comma in singleton tuples, due I believe to the use of expression parentheses rather than (say) the use of special brackets like chevrons. Something that I personally wish for is the ability to declare variable types 'up front' but that facility is missing from Python. This is an important issue, so I propose that the Python tutorial be updated to highlight such problems. I would be willing to write a draft section myself but obviously it would need to be reviewed. I am not sure if this is the appropriate place to make such a comment but it seems to be a good starting point. Any advice on making a more formal proposal would be welcome. Cheers, Richard Prosser PS Is it too late to fix such warts in version 3?

On 2011-12-10, at 15:16 , Richard Prosser wrote:
As far as I know, mutable default arguments have nothing to do with scoping, they have to do with the "toplevel" being evaluated fully, so as the function declaration is evaluated (to create the function object) so are its default arguments. This is independent from Python's scoping issues unless I misunderstood what you meant by "scoping". But this is definitely something which trips people. However, there is already a note (though a pretty low-key one, it should probably use an actual warning directive instead of just bolding it, you should submit a documentation patch) in the tutorial on that subject[0].
And technically, the irregularity with tuples is probably the empty tuple `()` as parens in other tuple arities are only necessary for disambiguation (much like parens around generator expressions): the "tuple constructor" is the comma, not the parens, a = 1, b = 1, 2 c = 1, 2, 3 are all valid and generate respectively a singleton, a pair and a triple. In that context, the trailing comma for singletons makes sense. If you want regularity, you can even add a trailing comma to the pair and the triple (as you can in e.g. a list or a dict): a = 1, b = 1, 2, c = 1, 2, 3, I'd rather have a lone comma (with or without parens, depending on the context) create a null tuple.
[0] http://docs.python.org/tutorial/controlflow.html#default-argument-values

On Sat, Dec 10, 2011 at 02:16:21PM +0000, Richard Prosser wrote:
Although I love Python there are some aspects of the language design which are disappointing and which can even lead to problems in some cases.
What really is disappointing is the number of people who criticize Python without knowing it.
You do not understand the syntax. Parens do not construct tuples - commas do. So for every tuple - even of length 1 - you must have a comma. The only exception is an empty tuple (of length 0).
Something that I personally wish for is the ability to declare variable types 'up front' but that facility is missing from Python.
You can use annotations. See PEP 3107. Twas implemented in Python 3.0. Oleg. -- Oleg Broytman http://phdru.name/ phd@phdru.name Programmers don't die, they just GOSUB without RETURN.

On Mon, Dec 12, 2011 at 2:38 PM, Masklinn <masklinn@masklinn.net> wrote:
<shameless> pyopt uses annotations as types for parsing command line options if that counts: http://code.google.com/p/pyopt/ </shameless> --Yuval

On Mon, 12 Dec 2011 08:00:28 -0500 Ned Batchelder <ned@nedbatchelder.com> wrote:
I think it would be more of a gotcha if parentheses were enough to create a tuple, though. Parentheses are useful to group operations, either for stylistic / syntactic support (think multi-line statements), or to work around operator precedence. Creating a tuple by mistake because you put some parentheses where not necessary would be really annoying. Regards Antoine.

On 12/12/2011 8:07 AM, Antoine Pitrou wrote: parens that make a tuple, but a comma." Then why when displaying a tuple does Python insist on using parens around it?
1, 2, 3 (1, 2, 3)
I'm not saying it shouldn't, it's a rhetorical question. The repr of a tuple always includes parens, even though "parens don't make a tuple." It's the best of all the options, but let's face it: it's confusing. --Ned.

Le lundi 12 décembre 2011 à 08:15 -0500, Ned Batchelder a écrit :
I would say: - because it's easier to read (subjectively so, I guess) - because it's easier to copy/paste into an expression without running into precedence problems Regards Antoine.

On 12/12/2011 8:19 AM, Antoine Pitrou wrote: > Le lundi 12 décembre 2011 à 08:15 -0500, Ned Batchelder a écrit : >> Believe me, I understand the issues. It is true, though that the >> single-element tuple syntax is often a surprise to people, and often >> well into their Python learning experience. We often repeat, "it isn't >> parens that make a tuple, but a comma." Then why when displaying a >> tuple does Python insist on using parens around it? >> >> >>> 1, 2, 3 >> (1, 2, 3) > I would say: > - because it's easier to read (subjectively so, I guess) > - because it's easier to copy/paste into an expression without running > into precedence problems Yes, those are good reasons. And you can see why Python's insistence on showing tuples with parens contributes to the gotcha that the parens are a red herring, and it's the commas that are important. Also, who hasn't said this to a beginner: "lists and tuples are very similar, lists use square brackets, tuples use parens"? Somehow, in a list, the commas don't make a tuple... It's complicated. I welcome Richard's help in explaining this issue to beginners. --Ned. > Regards > > Antoine. > > > _______________________________________________ > Python-ideas mailing list > Python-ideas@python.org > http://mail.python.org/mailman/listinfo/python-ideas

Here's some crazy ideas: * Don't allow iteration on tuples. for i in tuple * Make the singleton tuple the default object representation: (), a, (a, b), (a, b, c), ... * Steal STM, Channels, and sparks from Haskell * Profit. On Tue, Dec 13, 2011 at 12:26 AM, Ned Batchelder <ned@nedbatchelder.com> wrote:
-- ಠ_ಠ

On 2011-12-12, at 14:15 , Ned Batchelder wrote:
An alternative would be to just drop the literal unary tuple. I would be kind-of sad as I'd lose the ability to unpack singleton iterables, but it would "fix" the issue. FWIW, Haskell does not have a literal singleton (the standard defines "unit" `()` and 2-tuple through 15-tuple)

Masklinn wrote:
FWIW, Haskell does not have a literal singleton (the standard defines "unit" `()` and 2-tuple through 15-tuple)
That's because, due to its static typing, there is no reason you would ever need to use a 1-tuple rather than a bare value. We're not that lucky in Python, though. -- Greg

Greg Ewing writes:
I think you have misstated your point? That's not due to static typing, that's because you may *always* identify 1-factor products with the only factor, and Haskell made a deliberate decision to consistently represent the isomorphism class by the factor rather than the product. Eg, Python uses the same strategy for characters and strings (characters as a category are isomorphic to one-element strings), but chose a different representative (the one-element string rather than the character). As for Python's relative lack of luck, I think that's unlikely to be the reason why things are as they are. I'm pretty sure that Python's omission of character objects was deliberate<wink/>. I think it was the right choice, given that (1) despite category theory, I believe we think of tuples as composite objects (at least I do), but strings seem to be more "monolithic" in some sense, and (2) pretty much all languages have chosen to leave identifiers unmarked (I guess Perl and many template languages should be considered exceptions), and mark strings and characters (often both with quotation marks, sometimes characters with a prefix) -- may as well go with the familiar if there's no good reason otherwise.

On 12/13/2011 03:44 AM, Stephen J. Turnbull wrote:
Well, I would say the reason is that the type "tuple of any length" does not exist in Haskell. So there's no way you will have to pass a 1-tuple to a function that operates on tuples only. But of course, if we all used tuples as tuples only, we wouldn't have to do that either. It's only because we use tuples as sequences every so often. Georg

On 2011-12-15, at 22:00 , Ned Batchelder wrote:
What? The whole point of the "def foo(*args)" syntax is so a function can take an unknown-length list of arguments, which will be treated uniformly.
That's not *entirely* true, it's also used for proxy functions, in order to just pass a bunch of arguments (positional or keywords) along to the next level without having to unpack and repack them one by one.

Ned Batchelder writes:
That's way too high a level of abstraction. The discussion I found persuasive is the varargs analogy. /* "Hello Ned" in C */ #include <stdio.h> char *s = "string"; int i = 1; double x = 3.1415926; int main (int argc, char *argv[]) { printf ("string %s, int %d, double %f\nHello Ned! Are we homogenized yet?"); exit(-1); } Of course it still demands "correct perception" on the part of the user, but I don't see this as "inconsistent".

On 16 December 2011 07:42, Ned Batchelder <ned@nedbatchelder.com> wrote:
How is that inconsistent? At the point where the tuple is constructed, it has a known length. And it's definitely a heterogenous aggregation. I think where you're getting confused is that you're thinking of a *single* struct definition for every tuple. But the concept you should have is that each tuple has its own struct definition. And with functions, the structure is defined at function call time. Tim Delaney

On 12/15/2011 3:59 PM, Tim Delaney wrote:
Tim, this seems misguided to me. Finish that foo function definition: it will *have* to have "for a in args:" Since I don't know the length of args when I write the function, I have to treat it as an unknown length. What good is a "structure" that changes length and definition with every instance? I think you're trying too hard to fit the reality into the theory. --Ned.

On 12/15/2011 4:24 PM, Ethan Furman wrote:
I honestly don't know how to interpret this. In what way is it a "struct" if I have no idea how many fields it has, or how they might differ from each other? I think we've lost the utility of this discussion. --Ned.
~Ethan~

On Thu, 15 Dec 2011 16:54:16 -0500 Ned Batchelder <ned@nedbatchelder.com> wrote:
I do. Ethan is incorrectly trying to turn a similarity - that both Python tuples and C structs are collections of heterogeneous data - into an analogy. They aren't analogs, as that's pretty much the only similarity. If you really want a dynamic tuple - a dynamic collection of heterogeneous data accessed by an index - use a list. That was standard practice in LISP-like languages for a long time. But Python (and most modern LISPs) has better tools for such. For instance, the proper analog for a C struct in Python is the class. You provide definitions of them both along with names for their heterogeneous components. You create instances of them both that can assign different values to those components. The components are properly accessed by name. There are probably other similarities. There are a number of differences as well. Most notably, instances of classes are dynamic. You can change, and even add and delete, components dynamically. So Python already has a dynamic struct. You just have to get the analogies right. <mike -- Mike Meyer <mwm@mired.org> http://www.mired.org/ Independent Software developer/SCM consultant, email for more information. O< ascii ribbon campaign - stop html mail - www.asciiribbon.org

Ned Batchelder wrote:
Finish that foo function definition: it will *have* to have "for a in args:"
Not necessarily. It might be something like this: def foo(*args): if len(args) == 2: x, y = args fizzle2d(x, y) else: x, y, z = args fizzle3d(x, y, z) In other words, it might be what is effectively an overloaded function with a number of different possible calling signatures. Once you've decided which signature is being invoked, each member of the argument tuple takes on a specific role. -- Greg

Ned Batchelder wrote:
Where is that in the docs? Sounds like a patch is needed: "lists are for sequences where items need to be added/removed; tuples are for sequences/aggregations where items will not be added/removed once the tuple is created" ~Ethan~ PS As Antoine noted, a tuple for 'args' is appropriate, as once args is created at function call time, we won't be adding or removing from it.

On 12/15/2011 4:00 PM, Ethan Furman wrote:
I don't know if it appears in the docs, but I hear it all the time, and Guido has said it (http://mail.python.org/pipermail/python-dev/2003-March/033964.html): Tuples are for heterogeneous data, list are for homogeneous data. Tuples are *not* read-only lists. I don't want to get too far off the original point, which was: Python isn't as simple as we'd like to thing, and even smart beginners can be tripped up by things we've provided to them. --Ned.

On Fri, Dec 16, 2011 at 7:06 AM, Ned Batchelder <ned@nedbatchelder.com> wrote:
Guido has softened his stance on that point over the years (IIRC, the question came up explicitly in the discussion over making tuple() fully conform to the Sequence ABC - you can guess the outcome from the fact that tuple these days in fact *does* fully conform to that ABC, including the previously missing index() and count() methods). So tuples have two valid use cases: as read-only arbitrary-length sequences of homogeneous data and as fixed-length sequences of heterogeneous data. These days, the latter use case is often better served by creating a collections.namedtuple() definition rather than using a bare tuple directly. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On 12/15/2011 3:42 PM, Ned Batchelder wrote:
I have not been told that for several years, and I am pretty sure you will not find any such thing in the current docs. I consider it pretty much obsolete, as the differences that flowed from that idea are gone. In Python 3, tuples have all the non-mutating sequence methods that list does. The situation was much different in 1.4. -- Terry Jan Reedy

On Thu, Dec 15, 2011 at 5:16 PM, Terry Reedy <tjreedy@udel.edu> wrote:
I strongly disagree. Being immutable sequences (i.e. homogeneous) is a minor secondary role for tuples. Their primary role remains to hold a small bunch of heterogeneous values -- like namedtuple, but without needing forethought. A good example are dictionary items -- these are (key, value) pairs where for a given dict, the keys are all of the same type (or of a small set of related types) and ditto for the values, but the key type and the value types are unrelated. -- --Guido van Rossum (python.org/~guido)

On Thu, Dec 15, 2011 at 6:56 PM, Ned Batchelder <ned@nedbatchelder.com>wrote:
It's a historical accident. In ABC, functions didn't have multiple arguments -- instead there was a single argument that was a tuple. If you go back way, way in Python's early history (before 1.0 I think) you'll find that arguments kind of worked the same way. Then an idiom developed to accept a variable number of arguments, in order to support default argument values (because Python's tuples, unlike ABC's, were sequences even then). This turned out to be awkward if you wanted 1 or 2 arguments (the 1-arg case had to be dealt with special because you'd receive a plain value instead of a tuple) and then the *args and arg=value syntax was invented. But because previously varargs had been tuples, we kept them tuples. (I think this was also in the days that you couldn't unpack a list -- but I'm not actually sure of that. I do know that tuple unpacking was always in the language.) -- --Guido van Rossum (python.org/~guido)

Ned Batchelder <ned@nedbatchelder.com> writes:
I think that's a poor rendition of the distinction. Rather, what I advise is: if the *meaning* of an item depends on its position in the sequence (like with a C struct), use a tuple. If the meaning of an item is unaffected by its position in the sequence, use a list.
Then we define a function foo(*args), and Python gives us a tuple! :-(
Yes, exactly: the positional arguments to the function are *not* a homogeneous sequence, so a list doesn't connote the right thing. The position is important – we call them “positional arguments” – so a tuple makes sense. -- \ “I am the product of millions of generations of individuals who | `\ each fought against a hostile universe and won, and I aim to | _o__) maintain the tradition.” —Paul Z. Myers, 2009-09-12 | Ben Finney

Ned Batchelder <ned@nedbatchelder.com> writes:
On 12/15/2011 9:23 PM, Ben Finney wrote:
It's valuable for the case where the arguments need to be passed verbatim to a superclass. At some point, something in the call chain should know the meaning of each positional argument.
and you will of necessity treat them homogenously, no?
If the function consumes the positional arguments as a homogeneous list, I'd say that's a poor design (the function should instead specify a single argument which is a homogeneous sequence). -- \ “If you define cowardice as running away at the first sign of | `\ danger, screaming and tripping and begging for mercy, then yes, | _o__) Mr. Brave man, I guess I'm a coward.” —Jack Handey | Ben Finney

On 12/12/2011 8:15 AM, Ned Batchelder wrote:
Actually, I would agree that it would be better to not mislead by not printing the parens unless necessary. (If done from the beginning.) For something like
the outer parens are also unnecessary (while the inner ones are needed) and make the result less easy to read. But the easiest way to not print them would probably be to test the output string after it is constructed for beginning with '(' and ending with ')' and strip them off if so. -- Terry Jan Reedy

On 10 December 2011 14:16, Richard Prosser <richard.prosser@mail.com> wrote:
The default argument "problem" is not intuitive (and does need highlighting to people new to Python). But it is still better than the alternatives. It isn't to do with scoping but when the default arguments are evaluated. If instead the default values were evaluated at call time, what would the following code do: a = 3 def function(arg=a): pass del a function() In addition you have added extra overhead cost to each function call (argument evaluation). Plus Python exposes the default arguments to introspection. If the default arguments aren't evaluated until call time you lose that capability.
Well, parentheses are only part of the syntax for an empty tuple: () For non-empty tuple literals it is *only* the commas that are significant. single = 1, double = 2, 3 So although the syntax for a single member tuple does feel a bit awkward I wonder what alternative you would suggest?
Something that I personally wish for is the ability to declare variable types 'up front' but that facility is missing from Python.
That runs counter to the basic principles of Python where types are determined at runtime. What effect would declaring types have and how would that interact with the rest of the language? (Do you want every assignment to do a runtime type check?)
This is a good idea however. :-) Just do it. Create a documentation patch (for Python 3) and attach it to an issue in the bug tracker: http://bugs.python.org/
For the issues you've raised, yes. Unless you have specific proposals that don't break backwards compatibility. All the best, Michael Foord
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html

Hello Richard, On Sat, 10 Dec 2011 14:16:21 +0000 Richard Prosser <richard.prosser@mail.com> wrote:
I think documenting "gotchas" can be useful indeed. However, I'm not sure the tutorial is the right place: it should present an easy to grasp view of the language, not digress about edge cases. So perhaps a FAQ, for example, would be more appropriate. In any case, feel free to propose a draft on http://bugs.python.org You can take a look at http://docs.python.org/dev/documenting/ if you are not familiar with the process. Regards Antoine.

On 12/10/2011 9:16 AM, Richard Prosser wrote:
One of these things is not like the others. Mutable default arguments, and singleton tuples are surprises that make sense once you understand things on a deeper level. It makes sense to call them out as a "gotcha": a common stumbling block for learners. But "no type declarations" is not a wart, it's a fundamental feature of the language that is immediately apparent from the first lesson. --Ned.

On 2011-12-10, at 15:16 , Richard Prosser wrote:
As far as I know, mutable default arguments have nothing to do with scoping, they have to do with the "toplevel" being evaluated fully, so as the function declaration is evaluated (to create the function object) so are its default arguments. This is independent from Python's scoping issues unless I misunderstood what you meant by "scoping". But this is definitely something which trips people. However, there is already a note (though a pretty low-key one, it should probably use an actual warning directive instead of just bolding it, you should submit a documentation patch) in the tutorial on that subject[0].
And technically, the irregularity with tuples is probably the empty tuple `()` as parens in other tuple arities are only necessary for disambiguation (much like parens around generator expressions): the "tuple constructor" is the comma, not the parens, a = 1, b = 1, 2 c = 1, 2, 3 are all valid and generate respectively a singleton, a pair and a triple. In that context, the trailing comma for singletons makes sense. If you want regularity, you can even add a trailing comma to the pair and the triple (as you can in e.g. a list or a dict): a = 1, b = 1, 2, c = 1, 2, 3, I'd rather have a lone comma (with or without parens, depending on the context) create a null tuple.
[0] http://docs.python.org/tutorial/controlflow.html#default-argument-values

On Sat, Dec 10, 2011 at 02:16:21PM +0000, Richard Prosser wrote:
Although I love Python there are some aspects of the language design which are disappointing and which can even lead to problems in some cases.
What really is disappointing is the number of people who criticize Python without knowing it.
You do not understand the syntax. Parens do not construct tuples - commas do. So for every tuple - even of length 1 - you must have a comma. The only exception is an empty tuple (of length 0).
Something that I personally wish for is the ability to declare variable types 'up front' but that facility is missing from Python.
You can use annotations. See PEP 3107. Twas implemented in Python 3.0. Oleg. -- Oleg Broytman http://phdru.name/ phd@phdru.name Programmers don't die, they just GOSUB without RETURN.

On Mon, Dec 12, 2011 at 2:38 PM, Masklinn <masklinn@masklinn.net> wrote:
<shameless> pyopt uses annotations as types for parsing command line options if that counts: http://code.google.com/p/pyopt/ </shameless> --Yuval

On Mon, 12 Dec 2011 08:00:28 -0500 Ned Batchelder <ned@nedbatchelder.com> wrote:
I think it would be more of a gotcha if parentheses were enough to create a tuple, though. Parentheses are useful to group operations, either for stylistic / syntactic support (think multi-line statements), or to work around operator precedence. Creating a tuple by mistake because you put some parentheses where not necessary would be really annoying. Regards Antoine.

On 12/12/2011 8:07 AM, Antoine Pitrou wrote: parens that make a tuple, but a comma." Then why when displaying a tuple does Python insist on using parens around it?
1, 2, 3 (1, 2, 3)
I'm not saying it shouldn't, it's a rhetorical question. The repr of a tuple always includes parens, even though "parens don't make a tuple." It's the best of all the options, but let's face it: it's confusing. --Ned.

Le lundi 12 décembre 2011 à 08:15 -0500, Ned Batchelder a écrit :
I would say: - because it's easier to read (subjectively so, I guess) - because it's easier to copy/paste into an expression without running into precedence problems Regards Antoine.

On 12/12/2011 8:19 AM, Antoine Pitrou wrote: > Le lundi 12 décembre 2011 à 08:15 -0500, Ned Batchelder a écrit : >> Believe me, I understand the issues. It is true, though that the >> single-element tuple syntax is often a surprise to people, and often >> well into their Python learning experience. We often repeat, "it isn't >> parens that make a tuple, but a comma." Then why when displaying a >> tuple does Python insist on using parens around it? >> >> >>> 1, 2, 3 >> (1, 2, 3) > I would say: > - because it's easier to read (subjectively so, I guess) > - because it's easier to copy/paste into an expression without running > into precedence problems Yes, those are good reasons. And you can see why Python's insistence on showing tuples with parens contributes to the gotcha that the parens are a red herring, and it's the commas that are important. Also, who hasn't said this to a beginner: "lists and tuples are very similar, lists use square brackets, tuples use parens"? Somehow, in a list, the commas don't make a tuple... It's complicated. I welcome Richard's help in explaining this issue to beginners. --Ned. > Regards > > Antoine. > > > _______________________________________________ > Python-ideas mailing list > Python-ideas@python.org > http://mail.python.org/mailman/listinfo/python-ideas

Here's some crazy ideas: * Don't allow iteration on tuples. for i in tuple * Make the singleton tuple the default object representation: (), a, (a, b), (a, b, c), ... * Steal STM, Channels, and sparks from Haskell * Profit. On Tue, Dec 13, 2011 at 12:26 AM, Ned Batchelder <ned@nedbatchelder.com> wrote:
-- ಠ_ಠ

On 2011-12-12, at 14:15 , Ned Batchelder wrote:
An alternative would be to just drop the literal unary tuple. I would be kind-of sad as I'd lose the ability to unpack singleton iterables, but it would "fix" the issue. FWIW, Haskell does not have a literal singleton (the standard defines "unit" `()` and 2-tuple through 15-tuple)

Masklinn wrote:
FWIW, Haskell does not have a literal singleton (the standard defines "unit" `()` and 2-tuple through 15-tuple)
That's because, due to its static typing, there is no reason you would ever need to use a 1-tuple rather than a bare value. We're not that lucky in Python, though. -- Greg

Greg Ewing writes:
I think you have misstated your point? That's not due to static typing, that's because you may *always* identify 1-factor products with the only factor, and Haskell made a deliberate decision to consistently represent the isomorphism class by the factor rather than the product. Eg, Python uses the same strategy for characters and strings (characters as a category are isomorphic to one-element strings), but chose a different representative (the one-element string rather than the character). As for Python's relative lack of luck, I think that's unlikely to be the reason why things are as they are. I'm pretty sure that Python's omission of character objects was deliberate<wink/>. I think it was the right choice, given that (1) despite category theory, I believe we think of tuples as composite objects (at least I do), but strings seem to be more "monolithic" in some sense, and (2) pretty much all languages have chosen to leave identifiers unmarked (I guess Perl and many template languages should be considered exceptions), and mark strings and characters (often both with quotation marks, sometimes characters with a prefix) -- may as well go with the familiar if there's no good reason otherwise.

On 12/13/2011 03:44 AM, Stephen J. Turnbull wrote:
Well, I would say the reason is that the type "tuple of any length" does not exist in Haskell. So there's no way you will have to pass a 1-tuple to a function that operates on tuples only. But of course, if we all used tuples as tuples only, we wouldn't have to do that either. It's only because we use tuples as sequences every so often. Georg

On 2011-12-15, at 22:00 , Ned Batchelder wrote:
What? The whole point of the "def foo(*args)" syntax is so a function can take an unknown-length list of arguments, which will be treated uniformly.
That's not *entirely* true, it's also used for proxy functions, in order to just pass a bunch of arguments (positional or keywords) along to the next level without having to unpack and repack them one by one.

Ned Batchelder writes:
That's way too high a level of abstraction. The discussion I found persuasive is the varargs analogy. /* "Hello Ned" in C */ #include <stdio.h> char *s = "string"; int i = 1; double x = 3.1415926; int main (int argc, char *argv[]) { printf ("string %s, int %d, double %f\nHello Ned! Are we homogenized yet?"); exit(-1); } Of course it still demands "correct perception" on the part of the user, but I don't see this as "inconsistent".

On 16 December 2011 07:42, Ned Batchelder <ned@nedbatchelder.com> wrote:
How is that inconsistent? At the point where the tuple is constructed, it has a known length. And it's definitely a heterogenous aggregation. I think where you're getting confused is that you're thinking of a *single* struct definition for every tuple. But the concept you should have is that each tuple has its own struct definition. And with functions, the structure is defined at function call time. Tim Delaney

On 12/15/2011 3:59 PM, Tim Delaney wrote:
Tim, this seems misguided to me. Finish that foo function definition: it will *have* to have "for a in args:" Since I don't know the length of args when I write the function, I have to treat it as an unknown length. What good is a "structure" that changes length and definition with every instance? I think you're trying too hard to fit the reality into the theory. --Ned.

On 12/15/2011 4:24 PM, Ethan Furman wrote:
I honestly don't know how to interpret this. In what way is it a "struct" if I have no idea how many fields it has, or how they might differ from each other? I think we've lost the utility of this discussion. --Ned.
~Ethan~

On Thu, 15 Dec 2011 16:54:16 -0500 Ned Batchelder <ned@nedbatchelder.com> wrote:
I do. Ethan is incorrectly trying to turn a similarity - that both Python tuples and C structs are collections of heterogeneous data - into an analogy. They aren't analogs, as that's pretty much the only similarity. If you really want a dynamic tuple - a dynamic collection of heterogeneous data accessed by an index - use a list. That was standard practice in LISP-like languages for a long time. But Python (and most modern LISPs) has better tools for such. For instance, the proper analog for a C struct in Python is the class. You provide definitions of them both along with names for their heterogeneous components. You create instances of them both that can assign different values to those components. The components are properly accessed by name. There are probably other similarities. There are a number of differences as well. Most notably, instances of classes are dynamic. You can change, and even add and delete, components dynamically. So Python already has a dynamic struct. You just have to get the analogies right. <mike -- Mike Meyer <mwm@mired.org> http://www.mired.org/ Independent Software developer/SCM consultant, email for more information. O< ascii ribbon campaign - stop html mail - www.asciiribbon.org

Ned Batchelder wrote:
Finish that foo function definition: it will *have* to have "for a in args:"
Not necessarily. It might be something like this: def foo(*args): if len(args) == 2: x, y = args fizzle2d(x, y) else: x, y, z = args fizzle3d(x, y, z) In other words, it might be what is effectively an overloaded function with a number of different possible calling signatures. Once you've decided which signature is being invoked, each member of the argument tuple takes on a specific role. -- Greg

Ned Batchelder wrote:
Where is that in the docs? Sounds like a patch is needed: "lists are for sequences where items need to be added/removed; tuples are for sequences/aggregations where items will not be added/removed once the tuple is created" ~Ethan~ PS As Antoine noted, a tuple for 'args' is appropriate, as once args is created at function call time, we won't be adding or removing from it.

On 12/15/2011 4:00 PM, Ethan Furman wrote:
I don't know if it appears in the docs, but I hear it all the time, and Guido has said it (http://mail.python.org/pipermail/python-dev/2003-March/033964.html): Tuples are for heterogeneous data, list are for homogeneous data. Tuples are *not* read-only lists. I don't want to get too far off the original point, which was: Python isn't as simple as we'd like to thing, and even smart beginners can be tripped up by things we've provided to them. --Ned.

On Fri, Dec 16, 2011 at 7:06 AM, Ned Batchelder <ned@nedbatchelder.com> wrote:
Guido has softened his stance on that point over the years (IIRC, the question came up explicitly in the discussion over making tuple() fully conform to the Sequence ABC - you can guess the outcome from the fact that tuple these days in fact *does* fully conform to that ABC, including the previously missing index() and count() methods). So tuples have two valid use cases: as read-only arbitrary-length sequences of homogeneous data and as fixed-length sequences of heterogeneous data. These days, the latter use case is often better served by creating a collections.namedtuple() definition rather than using a bare tuple directly. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On 12/15/2011 3:42 PM, Ned Batchelder wrote:
I have not been told that for several years, and I am pretty sure you will not find any such thing in the current docs. I consider it pretty much obsolete, as the differences that flowed from that idea are gone. In Python 3, tuples have all the non-mutating sequence methods that list does. The situation was much different in 1.4. -- Terry Jan Reedy

On Thu, Dec 15, 2011 at 5:16 PM, Terry Reedy <tjreedy@udel.edu> wrote:
I strongly disagree. Being immutable sequences (i.e. homogeneous) is a minor secondary role for tuples. Their primary role remains to hold a small bunch of heterogeneous values -- like namedtuple, but without needing forethought. A good example are dictionary items -- these are (key, value) pairs where for a given dict, the keys are all of the same type (or of a small set of related types) and ditto for the values, but the key type and the value types are unrelated. -- --Guido van Rossum (python.org/~guido)

On Thu, Dec 15, 2011 at 6:56 PM, Ned Batchelder <ned@nedbatchelder.com>wrote:
It's a historical accident. In ABC, functions didn't have multiple arguments -- instead there was a single argument that was a tuple. If you go back way, way in Python's early history (before 1.0 I think) you'll find that arguments kind of worked the same way. Then an idiom developed to accept a variable number of arguments, in order to support default argument values (because Python's tuples, unlike ABC's, were sequences even then). This turned out to be awkward if you wanted 1 or 2 arguments (the 1-arg case had to be dealt with special because you'd receive a plain value instead of a tuple) and then the *args and arg=value syntax was invented. But because previously varargs had been tuples, we kept them tuples. (I think this was also in the days that you couldn't unpack a list -- but I'm not actually sure of that. I do know that tuple unpacking was always in the language.) -- --Guido van Rossum (python.org/~guido)

Ned Batchelder <ned@nedbatchelder.com> writes:
I think that's a poor rendition of the distinction. Rather, what I advise is: if the *meaning* of an item depends on its position in the sequence (like with a C struct), use a tuple. If the meaning of an item is unaffected by its position in the sequence, use a list.
Then we define a function foo(*args), and Python gives us a tuple! :-(
Yes, exactly: the positional arguments to the function are *not* a homogeneous sequence, so a list doesn't connote the right thing. The position is important – we call them “positional arguments” – so a tuple makes sense. -- \ “I am the product of millions of generations of individuals who | `\ each fought against a hostile universe and won, and I aim to | _o__) maintain the tradition.” —Paul Z. Myers, 2009-09-12 | Ben Finney

Ned Batchelder <ned@nedbatchelder.com> writes:
On 12/15/2011 9:23 PM, Ben Finney wrote:
It's valuable for the case where the arguments need to be passed verbatim to a superclass. At some point, something in the call chain should know the meaning of each positional argument.
and you will of necessity treat them homogenously, no?
If the function consumes the positional arguments as a homogeneous list, I'd say that's a poor design (the function should instead specify a single argument which is a homogeneous sequence). -- \ “If you define cowardice as running away at the first sign of | `\ danger, screaming and tripping and begging for mercy, then yes, | _o__) Mr. Brave man, I guess I'm a coward.” —Jack Handey | Ben Finney

On 12/12/2011 8:15 AM, Ned Batchelder wrote:
Actually, I would agree that it would be better to not mislead by not printing the parens unless necessary. (If done from the beginning.) For something like
the outer parens are also unnecessary (while the inner ones are needed) and make the result less easy to read. But the easiest way to not print them would probably be to test the output string after it is constructed for beginning with '(' and ending with ')' and strip them off if so. -- Terry Jan Reedy

On 10 December 2011 14:16, Richard Prosser <richard.prosser@mail.com> wrote:
The default argument "problem" is not intuitive (and does need highlighting to people new to Python). But it is still better than the alternatives. It isn't to do with scoping but when the default arguments are evaluated. If instead the default values were evaluated at call time, what would the following code do: a = 3 def function(arg=a): pass del a function() In addition you have added extra overhead cost to each function call (argument evaluation). Plus Python exposes the default arguments to introspection. If the default arguments aren't evaluated until call time you lose that capability.
Well, parentheses are only part of the syntax for an empty tuple: () For non-empty tuple literals it is *only* the commas that are significant. single = 1, double = 2, 3 So although the syntax for a single member tuple does feel a bit awkward I wonder what alternative you would suggest?
Something that I personally wish for is the ability to declare variable types 'up front' but that facility is missing from Python.
That runs counter to the basic principles of Python where types are determined at runtime. What effect would declaring types have and how would that interact with the rest of the language? (Do you want every assignment to do a runtime type check?)
This is a good idea however. :-) Just do it. Create a documentation patch (for Python 3) and attach it to an issue in the bug tracker: http://bugs.python.org/
For the issues you've raised, yes. Unless you have specific proposals that don't break backwards compatibility. All the best, Michael Foord
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html

Hello Richard, On Sat, 10 Dec 2011 14:16:21 +0000 Richard Prosser <richard.prosser@mail.com> wrote:
I think documenting "gotchas" can be useful indeed. However, I'm not sure the tutorial is the right place: it should present an easy to grasp view of the language, not digress about edge cases. So perhaps a FAQ, for example, would be more appropriate. In any case, feel free to propose a draft on http://bugs.python.org You can take a look at http://docs.python.org/dev/documenting/ if you are not familiar with the process. Regards Antoine.

On 12/10/2011 9:16 AM, Richard Prosser wrote:
One of these things is not like the others. Mutable default arguments, and singleton tuples are surprises that make sense once you understand things on a deeper level. It makes sense to call them out as a "gotcha": a common stumbling block for learners. But "no type declarations" is not a wart, it's a fundamental feature of the language that is immediately apparent from the first lesson. --Ned.
participants (18)
-
Antoine Pitrou
-
Ben Finney
-
Ethan Furman
-
Georg Brandl
-
Greg Ewing
-
Guido van Rossum
-
Masklinn
-
Matt Joiner
-
Michael Foord
-
Mike Meyer
-
Ned Batchelder
-
Nick Coghlan
-
Oleg Broytman
-
Richard Prosser
-
Stephen J. Turnbull
-
Terry Reedy
-
Tim Delaney
-
Yuval Greenfield