tuples tuplas - Semántica del desempaquetado de la tupla en python.




vacia lista (6)

¿Por qué python solo permite que los argumentos con nombre sigan una expresión de desempaquetado de tuplas en una llamada de función?

>>> def f(a,b,c):
...     print a, b, c
... 
>>> f(*(1,2),3)
  File "<stdin>", line 1
SyntaxError: only named arguments may follow *expression

¿Es simplemente una elección estética, o hay casos en los que permitir esto llevaría a algunas ambigüedades?


Answers

En primer lugar, es simple proporcionar una interfaz muy similar utilizando una función de envoltura:

def applylast(func, arglist, *literalargs):
  return func(*(literalargs + arglist))

applylast(f, (1, 2), 3)  # equivalent to f(3, 1, 2)

En segundo lugar, mejorar el intérprete para que admita su sintaxis de forma nativa puede agregar sobrecarga a la actividad muy crítica de la aplicación de la función. Incluso si solo requiere unas cuantas instrucciones adicionales en el código compilado, debido al alto uso de esas rutinas, eso podría constituir una penalización de rendimiento inaceptable a cambio de una característica que no es muy frecuente en una biblioteca de usuarios.


cambiar el orden:

def f(c,a,b):
    print(a,b,c)
f(3,*(1,2))

Estoy bastante seguro de que la razón por la que a las personas "naturalmente" no les gusta esto es porque hace que el significado de los argumentos posteriores sea ambiguo, dependiendo de la longitud de la serie interpolada:

def dangerbaby(a, b, *c):
    hug(a)
    kill(b) 

>>> dangerbaby('puppy', 'bug')
killed bug
>>> cuddles = ['puppy']
>>> dangerbaby(*cuddles, 'bug')
killed bug
>>> cuddles.append('kitten')
>>> dangerbaby(*cuddles, 'bug')
killed kitten

no se puede decir con solo mirar las dos últimas llamadas a dangerbaby cuál funciona como se esperaba y cuál mata a la gatita fluffykins.

Por supuesto, parte de esta incertidumbre también está presente cuando se interpola al final. pero la confusión está restringida a la secuencia interpolada, no afecta a otros argumentos, como el bug .

[Hice una búsqueda rápida para ver si podía encontrar algo oficial. Parece que el prefijo * para varags se introdujo en Python 0.9.8 . La sintaxis anterior se discute aquí y las reglas de cómo funcionó fueron bastante complejas. ya que la adición de argumentos adicionales "tenía que" ocurrir al final cuando no había un marcador *, parece que eso simplemente continuó. finalmente, se menciona aquí una larga discusión sobre listas de argumentos que no fue por correo electrónico.]


Sospecho que es por coherencia con la notación en estrella en las definiciones de funciones, que es, después de todo, el modelo para la notación en estrella en las llamadas a funciones.

En la siguiente definición, el parámetro *c absorberá todos los argumentos subsiguientes que no sean de palabras clave, por lo que, obviamente, cuando se llame a f , la única forma de pasar un valor para d será como un argumento de palabra clave.

def f(a, b, *c, d=1):
    print "slurped", len(c)

(Tales "parámetros de palabras clave solamente" solo se admiten en Python 3. En Python 2 no hay manera de asignar valores después de un argumento destacado, por lo que lo anterior es ilegal).

Por lo tanto, en una definición de función, el argumento destacado debe seguir todos los argumentos posicionales ordinarios. Lo que observaste es que la misma regla se ha extendido a las llamadas de función. De esta manera, la sintaxis en estrella es consistente para declaraciones de funciones y llamadas de funciones.

Otro paralelismo es que solo puede tener un argumento destacado (único) en una llamada a función. Lo siguiente es ilegal, aunque uno podría fácilmente imaginar que está permitido.

f(*(1,2), *(3,4))

Algunas observaciones:

  1. Python procesa los argumentos posicionales antes de los argumentos de palabras clave ( f(c=3, *(1, 2)) en su ejemplo, aún se imprime 1 2 3 ). Esto tiene sentido ya que (i) la mayoría de los argumentos en las llamadas a funciones son posicionales y (ii) la semántica de un lenguaje de programación debe ser inequívoca (es decir, la elección debe hacerse de cualquier manera en el orden en que se procesan los argumentos posicionales y de palabras clave) ).
  2. Si tuviéramos un argumento posicional a la derecha en una llamada de función, sería difícil definir lo que eso significa. Si llamamos f(*(1, 2), 3) , ¿debería ser f(1, 2, 3) o f(3, 1, 2) y por qué cualquier opción tendría más sentido que la otra?
  3. Para una explicación oficial, PEP 3102 proporciona mucha información sobre cómo funcionan las definiciones de funciones. La estrella (*) en una definición de función indica el final de los argumentos de posición (sección Especificación ). Para ver por qué, considere: def g(a, b, *c, d) . No hay forma de proporcionar un valor para d no sea como un argumento de palabra clave (los argumentos posicionales serían 'grabados' por c ).
  4. Es importante darse cuenta de lo que esto significa: cuando la estrella marca el final de los argumentos posicionales, eso significa que todos los argumentos posicionales deben estar en esa posición o a la izquierda de la misma.

Para Python 2.5 y posteriores hay una sintaxis específica:

[on_true] if [cond] else [on_false]

En Pythons más antiguos no se implementa un operador ternario, pero es posible simularlo.

cond and on_true or on_false

Sin embargo, hay un problema potencial, que si cond evalúa como True y on_true evalúa como False , se devuelve on_true lugar de on_true . Si desea este comportamiento, el método es correcto, de lo contrario use esto:

{True: on_true, False: on_false}[cond is True] # is True, not == True

el cual puede ser envuelto por:

def q(cond, on_true, on_false)
    return {True: on_true, False: on_false}[cond is True]

y utilizado de esta manera:

q(cond, on_true, on_false)

Es compatible con todas las versiones de Python.





python tuples iterable-unpacking