Propose Review of Single Element Tuple Handling in Python

Issue Overview
In Python, there’s an ambiguous behavior when dealing with tuples containing a single element. Specifically, assigning a value like python = 1, results in python being treated as a tuple (1,). This behavior can be non-intuitive, especially for beginners, and lacks an explicit error message, potentially leading to unintended bugs.

Proposed Solution

  • Introduce a clearer error message or warning when creating single-element tuples.
  • Enhance the documentation on this behavior to prevent confusion among new Python users.

Expected Outcome

  • Developers will have a clearer understanding of Python’s behavior when creating single-element tuples.
  • Reduce the risk of accidental errors among beginners due to this unique syntax.

Additional Information

  • This issue is particularly confusing for those new to Python.
  • Current Python documentation does not sufficiently explain this behavior.
1 Like

There are an untold number of programs that rely on this behavior. It certainly cannot become an error, so the only choice would be to make it a warning forever. And I don’t think that’s a good idea: most of the people who see these warnings couldn’t do anything about it.

I think this is best left to linters to warn about.

18 Likes

It’s not ambiguous; it does the same thing in every situation. It’s also by design. It lacks an explicit error message, yes. It lacks any kind of error or warning message, because it is considered correct code. It’s a natural consequence of a) the fact that a comma is needed to indicate a tuple (since letting (x) mean anything different from just x would cause all kinds of other problems) plus b) the convenience of not requiring parentheses for a tuple (which, in turn, is why you can write x, y = y, x without parentheses).

Some may find it not intuitive. But I ask: what else might one expect it to do?

It’s documented all over the place: in the tutorial, in the detailed language reference for expressions (the syntax 1, is an expression_list), again in the full grammar specification in the language reference, in the built-in types section of the library reference, on the wiki, and on Stack Overflow.

It’s also fairly hard to do this by accident. , isn’t a common typo for ;, for example, and “you don’t need semicolons” is one of the big selling points of Python, so beginners coming from other language backgrounds should be trained out of the bad habit as quickly as possible. I suppose that in the specific example of 1,, that could be a typo for 1. - I recommend always typing at least one explicit decimal digit for floating-point values, thus, 1.0. That’s how you write it in a math classroom, anyway.

5 Likes

There is no error, and I do not see any ambiguity in the example. However, in the section where tuples are defined, I think the phrase about one-item tuples, " The trailing comma is required only to create a single tuple (a.k.a. a singleton)", would be improved by replacing “single” with “one-item”, as “single tuple” really means “one tuple (of whatever length)”. I also think a minimal example, such as “such as 1,” would help. I would also replace ‘;’ with ‘.’ and make the second phrase a separate sentence. The result would be " A trailing comma is required only to create a one-item tuple (a.k.a. a singleton), such as 1,. (I believe the .rst markup would need double backticks instead of single backticks for the example.)

EDIT: I will look at the tutorial tomorrow. (Karl’s first post with a link (thanks) was posted while I wrote the original version of this line.)

2 Likes

It’s in the links in my post :wink:

Also, syntax warnings are best used either as a temporary measure until something becomes an error or for dodgy, rare, and unneeded syntax: Example:

>>> 0x1for i in ''
<stdin>:1: SyntaxWarning: invalid hexadecimal literal
31

parsed as 0x1f or (i in '') which is 31 or False, which is 31. One item tuples are not in this category.

1 Like

Related previous topic:

1 Like

This is something that linters could choose to respond to, but making it an error would break far too much valid code, not to mention that (aside from the empty tuple) it’s never the parentheses that create a tuple.

This PR edits the one-item tuple doc to be clearer. The tutorial looked fine as it already said more and included such in its code examples. The PR and 2 backports are merged.

2 Likes

Sensible improvements! I love watching the cpython commits. I’ve learned a lot.

I’m sure it’s been discussed- but why not create an explicit syntax for a one-item tuple, that’s easier on the eyes than a dangling comma?

Going further; an opt-in way to warn or reject the comma syntax on a module or package would be welcome. I’d use it.

The comma is a beautiful thing however it’s working overtime here.

A problem example when writing: dataclasses have no commas after fields while a traditional init does; so cut and paste in a big project with a mix of class declaration styles can easily introduce an erroneous comma.

When reading: Documentation/samples for pickle’s reduce often show a one-item tuple with the telltale comma and lots of parentheses, which is offf-putting and obfuscating to beginners. Something clean that jumps out and says “I’m a one-tuple” would really be nice.

We have one! It looks like this: (value,)

This is the way most people make it look clearer. What else is needed?

3 Likes

Python has embraced aesthetics from the beginning, and that’s a big part of its success.
Beginners, intermediates and experts have different aesthetic views. (The Oxford comma is a similar debate :slight_smile: )

I tried to learn Scala but they overused the underscore.

I tried to like yaml but they use too many one-character idioms.

Python is beautiful. I think the one-tuple comma is fine but an explicit syntax that’s not reliant on commas and parentheses might be better for many people. Non-experts of course.

I can create my own one-item tuple constructor (and I think I will) but there are advantages to standard official syntax, to help non-experts with reading, writing and understanding.

And what would that syntax be? Would that syntax be only for single element tuples? Or would this introduce a new syntax for all tuples?

1 Like

Many possibilities. How about adding a keyword argument to the tuple() builtin? (For the single-item tuple curiosity)

I don’t understand. How do the parentheses and trailing comma not “jump out”? What could it, in principle, look like instead that is more evident? Parentheses without a trailing comma are already needed for expression grouping; square brackets already mean a list; braces already mean a dict or set; angle brackets are already comparison operators and would be ferociously hard to parse any other way at the same time. If a trailing comma isn’t acceptable, then we can’t fix the problem with an element separator, only with delimiters for the tuple; but every sensible option is already used. And at any rate I don’t understand why any such option would be more visually distinctive.

Better IMO to use a variable-args constructor, then you can have a uniform appearance:

def tuple_of(*args):
    return args

I strongly doubt that tuple(x, single=True) is any better than tuple([x]) or tuple((x,)).

For the keyword arg form, I meant:

tuple(item=x)

That looks good to me.

Subjectively, that looks worse to me.

Objectively, it replaces an unambiguous syntactic construct with a function call whose semantics can be changed at runtime by rebinding the name tuple.

It also makes the existing type responsible for too many separate tasks. Currently, tuple does one thing: constructs an instance of tuple using an iterable value. Since were already increasing the amount of typing, a class method like

tuple.from_items(x)
# 6 characters longer than
# tuple(item=x)
# which is 9 characters longer than
# (x,)

would be more explicit and still generalize to tuples of arbitrary size:

 @classmethod
 def from_elements(cls, *args):
     return cls(args)
1 Like

The barrier for new syntax and forms, including behaviors for built-in types, is rightly quite high.

A successful change proposal requires stronger justification than “it looks nice to me”. Aesthetics are important, but they are too subjective, on their own, to provide us with directions for the language. You say “I like it” and I say “I don’t like it” and the conversation goes nowhere because we aren’t really sending much information back and forth.

There are already two ways to do it:

x = 1,
y = (1,)

This would add a third.

A larger core language isn’t necessarily better. Often it’s worse just by virtue of being larger. Python is pretty large already, as a language (not the biggest, but certainly not the smallest). Sometimes I wish it were smaller. I rarely wish for it to be bigger.

1 Like

Python has too much history / existing code for a change to tuple syntax make sense. There should be one-- and preferably only one --obvious way to do it. Adding another single tuple construction option would just confuse a new Python developer who comes across code using the existing syntax.

But let’s not pretend for a moment that the x = (4,) syntax is not just weird from a natural language (English) standpoint. It looks like a typo. It looks like a superfluous comma analogous to the trailer in (3,4,5) == (3,4,5,) .

When I write tuples my mantra is the comma makes the tuple, because I’ve burned myself many times forgetting it.

So let’s keep the existing syntax with empathy and kindness for new developers tripped up by it.

3 Likes

120% agree. I doubt anyone would argue too strongly that the way of spelling a tuple which we have is the best, especially for beginners.

But adding a new alternative doesn’t necessarily improve the situation.

4 Likes