2012-05-23 17 views
28

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

>>> 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 que permitir esto daría lugar a algunas ambigüedades?

+1

Puede agregar el último argumento a la tupla antes de desempacar. – JBernardo

+2

Una pregunta interesante. Parece que recuerdo las reglas cambiadas en torno a python 2.5. Antes solía * tener * para poner los '* args' y' ** kwargs' al final, pero no puedo encontrar la entrada del registro de cambios. –

Respuesta

28

Estoy bastante seguro de que la razón por la gente "naturalmente" no les gusta que esto es porque hace que el significado de los argumentos posteriores ambigua, 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 de solo mirar las últimas dos llamadas al dangerbaby cuál funciona como se espera y cuál mata al pequeño gatito esponjoso.

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

[Hice una búsqueda rápida para ver si podía encontrar algo oficial. parece que el * prefijo para varags era introduced in python 0.9.8. la sintaxis anterior es discussed here y las reglas de cómo funcionó fueron bastante complejas. dado que la adición de argumentos adicionales "tenía que" ocurrir al final cuando no había un marcador *, parece que simplemente se transfirió. finalmente hay un mention here de una larga discusión sobre listas de argumentos que no fue por correo electrónico.]

+0

+1 Me gusta este ejemplo: D – jamylak

0

cambio de la orden:

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

Entiendo cómo funciona, tengo curiosidad por * por qué *. – user545424

+0

Bueno, es una regla que los argumentos deben seguir este orden: '(args, name = args, * args, ** args)' –

+6

Sí, pero *** ¿por qué *** es una regla? –

1

En primer lugar, es simple para proporcionar una interfaz muy similar a sí mismo utilizando una función de contenedor:

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

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

En segundo lugar, la mejora de la intérprete para apoyar su sintaxis de forma nativa puede agregar sobrecarga a la actividad de actividad de rendimiento muy crítica. Incluso si solo requiere unas pocas instrucciones adicionales en el código compilado, debido al alto uso de esas rutinas, eso podría constituir una penalización inaceptable del rendimiento a cambio de una característica que no se necesita para todas las veces que se acomoda fácilmente en una biblioteca de usuario.

6

Sospecho que es por consistencia 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 llamadas a funciones.

En la definición siguiente, el parámetro *c se sorber todos los argumentos que no sean de palabras clave posteriores, por lo que, obviamente, cuando f se llama, la única manera 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 de sólo" están sólo está soportado en Python 3. En Python 2 hay es hay manera de asignar valores después de un argumento estrellado, por lo que la anterior es ilegal.)

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

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

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

@Jeff, obviamente está usando python 2, que no tiene parámetros de palabras clave. Actualizado para deletrear esto. – alexis

+0

¡Lo siento! Todavía no he encontrado todas las diferencias entre python 2 y 3, supongo. –

+0

eliminado mi (no útil) comentario anterior. –

1

Algunas observaciones:

  1. Python procesa argumentos posicionales antes de argumentos de palabra clave (f(c=3, *(1, 2)) en su ejemplo sigue imprimiendo 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, debe hacerse una elección en el orden en que se procesen los argumentos posicionales y de palabra clave)
  2. Si tuviéramos un argumento posicional a la derecha en una llamada a función, sería difícil definir lo que eso significa. Si llamamos al f(*(1, 2), 3), ¿debería ser f(1, 2, 3) o f(3, 1, 2) y por qué cualquiera de las dos opciones tendría más sentido que la otra?
  3. Para una explicación oficial, PEP 3102 proporciona una gran cantidad de información sobre cómo funcionan las definiciones de funciones. La estrella (*) en una función definición indica los argumentos de fin 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 que no sea como un argumento de palabra clave (los argumentos posicionales serían 'agarrados' por c).
  4. Es importante darse cuenta de lo que esto significa: como 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.
0

Si usted tiene un parámetro Python 3 solamente palabras clave, como

def f(*a, b=1): 
    ... 

entonces se podría esperar algo así como f(*(1, 2), 3) para establecer a a (1 , 2) y b-3, pero, por supuesto, aunque la sintaxis lo que quiere es que se permita, no lo haría, porque los parámetros de palabra clave solo deben ser de palabras clave, como f(*(1, 2), b=3). Si estuviera permitido, supongo que tendría que establecer a en (1, 2, 3) y dejar b como el predeterminado 1. Así que tal vez no sea ambigüedad sintáctica sino ambigüedad en lo que se espera, que es algo que Python intenta evitar en gran medida.

Cuestiones relacionadas