2012-06-05 15 views
7

considerar estos comportamientos diferentes ::explique por qué estas dos funciones incorporadas se comportan diferente cuando pasan argumentos de palabra clave en

>> def minus(a, b): 
>> return a - b 

>> minus(**dict(b=2, a=1)) 
-1 

>> int(**dict(base=2, x='100')) 
4 

>> import operator 
>> operator.sub.__doc__ 
'sub(a, b) -- Same as a - b.' 
>> operator.sub(**dict(b=2, a=1)) 
TypeError: sub() takes no keyword arguments 

¿Por qué operator.sub comportamiento diferente del de int(x, [base])?

+3

Sería bueno si las personas también pudieran responder por qué esta función elige no utilizar argumentos de palabra clave, me gustaría saber esto también: D – jamylak

Respuesta

7

Es un detalle de implementación. El Python C API to retrieve arguments separa los argumentos posicionales y de palabra clave. Los argumentos posicionales ni siquiera tienen un nombre internamente.

El código utilizado para recuperar los argumentos de las funciones operator.add (y otros similares como sub) es la siguiente:

PyArg_UnpackTuple(a,#OP,2,2,&a1,&a2) 

Como se puede ver, no contiene ningún nombre de argumento. Todo el código relacionado con operator.add es:

#define spam2(OP,AOP) static PyObject *OP(PyObject *s, PyObject *a) { \ 
    PyObject *a1, *a2; \ 
    if(! PyArg_UnpackTuple(a,#OP,2,2,&a1,&a2)) return NULL; \ 
    return AOP(a1,a2); } 

spam2(op_add   , PyNumber_Add) 
#define spam2(OP,ALTOP,DOC) {#OP, op_##OP, METH_VARARGS, PyDoc_STR(DOC)}, \ 
          {#ALTOP, op_##OP, METH_VARARGS, PyDoc_STR(DOC)}, 
spam2(add,__add__, "add(a, b) -- Same as a + b.") 

Como se puede ver, el único lugar donde a y b se utilizan en la cadena de documentación. La definición del método tampoco usa el indicador METH_KEYWORDS que sería necesario para que el método acepte argumentos de palabra clave.

en general se habla, se puede asumir con seguridad que una función basada en Python donde se sabe que un nombre de argumento se siempre aceptar los argumentos de palabras clave (por supuesto, alguien podría hacer cosas desagradables con *args desembalaje pero la creación de un documento de la función donde los argumentos se ven normal) mientras que las funciones C pueden o no aceptar argumentos de palabra clave. Es probable que las funciones con más de unos pocos argumentos o argumentos opcionales acepten argumentos de palabra clave para los posteriores/opcionales. Pero tienes que probarlo.

Puede encontrar un debate acerca de la compatibilidad de argumentos de palabra clave en todas partes en el python-ideas mailinglist. También hay una declaración de Guido van Rossum (el Benevolent Dictator For Life también conocido como el creador de Python) en él:

Hm. Creo que para muchas (¿la mayoría?) 1-arg y funciones 2-arg seleccionadas (y raramente funciones 3 + -arg) esto reduciría la legibilidad, como se muestra en el ejemplo de ord (char = x).

De hecho, me gustaría ver una función sintáctica afirmar que un argumento no puede darse como un argumento de palabra clave (del mismo modo que ya sintaxis para declarar que debe ser una palabra clave es nuestro).

Un área donde creo que agregar args de palabra clave es rotundamente erróneo: métodos de tipos incorporados o ABC y que son reemplazables. P.ej. considere el método pop() en dict.Como el nombre del argumento actualmente es no documentado, si alguien subclasifica dict y anula este método, o si crean otra clase de mapeo mutable que intente emular dict usando pato escribiendo, no importa cuál sea el nombre del argumento - todas las personas que llaman (que esperan una dict, una subclase dict o un Dict-like duck) usarán argumentos posicionales en la llamada. Pero si fuéramos para documentar los nombres de los argumentos para pop(), y los usuarios comenzaran a usar éstos, entonces la mayoría de los subtítulos y patos se romperían repentinamente (excepto si por casualidad eligieran el mismo nombre).

+0

+1 ¡Esto también responde mi pregunta! – jamylak

+0

Más allá de la elección estilística, debo señalar que la aceptación de argumentos de palabras clave agrega sobrecarga a la llamada a la función, y si el usuario realmente pasa los argumentos por palabra clave a una función incorporada, la sobrecarga aumenta de manera significativa. Microbenchmarks en Python 3.5 para 'int (" 0 ", 2)' contra 'int (" 0 ", base = 2)' en mi máquina muestran que el último tarda ~ 2.5x en ejecutarse; se gasta más tiempo analizando los argumentos que en el trabajo real. Peor aún son las funciones que toman un solo argumento, que tienen una ruta rápida que evita el envolvimiento de argumentos, pero moverse a palabras clave significa que los argumentos deben estar envueltos en un 'tuple' y/o' dict'. – ShadowRanger

3

minus(**dict(b=2, a=1)) se expande a minus(b=2, a=1). Esto funciona porque su definición tiene nombres de argumento a y b.

operator.sub(**dict(b=2, a=1)) se expande a operator.sub(b=2, a=1). Esto no funciona porque sub no acepta argumentos de palabra clave.

+2

Estoy bastante seguro de que está preguntando por qué no acepta los argumentos de palabra clave y usted no he respondido eso. – jamylak

+0

¿Está diciendo que operator.sub no tiene sus argumentos nombrados como 'menos'? – canadadry

+0

@BonAmi: Exactamente. Los argumentos de 'operator.sub' no tienen nombre – Eric

4

operator es un módulo C, que defines functions differently. A menos que la declaración de función en la inicialización del módulo incluya METH_KEYWORDS, la función no aceptará argumentos de palabras clave bajo ninguna circunstancia y obtendrá el error dado en la pregunta.

Cuestiones relacionadas