¿Por qué python eligió las comas entre paréntesis en el diseño de la tupla?

De python wiki

Tuplas de elementos múltiples

En Python, las tuplas de elementos múltiples se parecen a:

1,2,3

pero nuevamente, son las comas, no los paréntesis, los que definen la tupla.

¡¿Oh enserio?!

Entonces por qué:

 >>> tuple((((((1, 2, 3)))))) # creates a valid tuple # (1, 2, 3) >>> tuple(1, 2, 3, ) # But not here # TypeError: tuple() takes at most 1 argument (3 given) 

Más en serio, no entiendo por qué el paréntesis no fue elegido sobre las comas?

Porque creo que crearía una paradoja cuando:

 >>> 1, # is a valid tuple # (1,) >>> tuple([1]) # Or this # (1,) >>> tuple(1) # But not this one # TypeError: 'int' object is not iterable 

Sin embargo, si considera que los paréntesis siempre estuvieron a cargo de la creación de instancias de una tuple , todos los problemas de la creación de instancias de tuple con elementos múltiples desaparecerán.

por ejemplo, en mi mundo imaginario:

 >>> (1, 2, 3) # stay valid # (1, 2, 3) >>> (1) # is newly valid # (1) >>> () # stay valid # () >>> 1, # TypeError: int() takes exactly 1 argument (2 given) 

Sé que esta es una característica bien conocida y ya lo siento si es un duplicado. He encontrado muchos temas similares sobre cómo funcionaba la tupla, pero ninguno explica en detalle por qué esta función se creó de esa manera.

También soy consciente de que este tema podría cerrarse en función de la opinión, pero estoy más interesado en razones técnicas (si las hay), o al menos en razones históricas .

Gracias

Este es un artefacto de la gramática .

Los términos separados por comas son un bloque de construcción para las tuplas, listas y conjuntos, dependiendo de si están envueltos entre corchetes, llaves o nada en absoluto.

El principal desafío al especificar una gramática es equilibrar los múltiples usos competitivos de los mismos personajes. Las comas separan partes de listas, tuplas, dados y conjuntos. Las comas también separan los argumentos en las llamadas a funciones. Se permiten comas finales para ambos usos (y se requieren para tuplas de longitud uno). Del mismo modo, los paréntesis tienen múltiples usos, incluyendo llamadas a funciones y agrupación para expresiones aritméticas. El período sirve como punto decimal y para el operador getattribute.

La razón por la que tuple(..) no toma múltiples argumentos, es porque puede haber confusión entre (1) un solo elemento con un iterable que debe convertirse en una tupla, y (2) múltiples argumentos.

Digamos que llama tupla con un solo elemento iterable, entonces puede haber confusión sobre si Python debería construir una tupla con un elemento: el iterable; o una tupla que se construye con base en el iterable dado. Los diseñadores de Python eligieron este último. Como se especifica en la documentación :

tuple(iterable)

Esto es razonable ya que entonces uno puede escribir:

 tuple(x*x for x in somelist if x%2) 

Por ejemplo, para construir una tupla con cuadrados de elementos impares de la lista somelist .

En tu primer ejemplo:

 tuple((((((1, 2, 3)))))) 

Has construido una tupla (1, 2, 3) . Ahora pasas esa tupla al constructor de tuplas como único parámetro. Como una tupla es una iterable, construyes una tupla basada en la única iterable que pasas.

Tu expresion

 tuple(1, 2, 3, ) 

Sin embargo, genera un error, ya que proporciona más elementos que el único que puede y debe aprobar.

A continuación tienes el tuple literal . Un literal es una lista de expresiones separadas por comas. Construye una tupla con los elementos dados. Sin embargo, en caso de que llame a un método sin paréntesis adicional, existe una confusión entre (1) un literal de tupla; y (2) múltiples argumentos que pasas al método.

Hay otros aspectos de “gramática” en Python. Por ejemplo, si pasa un generador a una función con múltiples argumentos, el generador también debe estar entre paréntesis.

Por lo general, los parens no sirven para nada más que agrupar, pero cuando aparecen después de una llamada, tienen una connotación diferente que solo agrupar expresiones; Python, a diferencia de, dice Haskell, requiere que esos parens hagan la llamada.

En el siguiente ejemplo, el par externo de parens está vinculado a la llamada de función (construcción de lenguaje), mientras que los nesteds agrupan la expresión interna (los argumentos de función) como una tupla:

 >>> tuple((1, 2, 3)) (1, 2, 3) 

Esencialmente, la tupla es creada por la (s) coma (s).