diferencias entre PyArg_ParseTuple y PyArg_ParseTupleAndKeywords?

Gabriel Genellina gagsl-py2 en yahoo.com.ar
Sab Mayo 3 01:47:44 CEST 2008


En Thu, 01 May 2008 20:37:20 -0300, Milton Galo Patricio Inostroza  
Aguilera <minoztro en gmail.com> escribió:

>    Me he animado a aprender a programar modulos en c para python {para
> poder luego entender la implementacion misma del lenguaje}...y me he
> encontrado con estas dos funciones.
>
>    En realidad no noto cual es la diferencia de uso de estas dos
> funciones, si bien es cierto que tienen una declaracion distintas
> (argumentos), en la utilizacion de estas no puedo ver la diferencia
> entre una y otra...muestro un ejemplo {que es el que esta en el manual
> de extending and embedding python}:

PyArg_ParseTuple solo procesa argumentos posicionales. Es decir, a la  
funcion hay que pasarle una tupla con los argumentos en el orden exacto en  
que estan declarados. La funcion en C tiene normalmente esta forma:

PyObject *
funcion(PyObject *self, PyObject *args)
{
   ...
}

En Python es como si hubiera sido declarada asi:

def funcion(*args):

PyArg_ParseTupleAndKeywords procesa argumentos posicionales y nominales.  
Los argumentos posicionales se pasan en una tupla; los nominales en un  
diccionario. En C la funcion normalmente esta declarada asi:

PyObject *
funcion(PyObject *self, PyObject *args, PyObject *kwds)
{
   ...
}

En Python es como si la declaracion hubiera sido:

def funcion(*args, **keywds):

Si la funcion espera argumentos por posicion exclusivamente (es decir, usa  
PyArg_ParseTuple), NO se le pueden pasar por nombre. Ejemplo:

py> help(file.seek)
Help on method_descriptor:

seek(...)
     seek(offset[, whence]) -> None.  Move to new file position.

     Argument offset is a byte count.  Optional argument whence [...]

Ahi dice que el primer argumento de seek se llama offset. Pero no se lo  
podemos pasar por nombre:

py> open("un/archivo/que/exista").seek(offset=123)
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
TypeError: seek() takes no keyword arguments

Mirando la documentacion exclusivamente, NO hay forma de saber si una  
funcion espera ser llamada con argumentos posicionales o nominales.

> static PyObject *
> keywdarg_parrot(PyObject *self, PyObject *args, PyObject *keywds)
> {
>     int voltage;
>     char *state = "a";
>     char *action = "a";
>     char *type = "a";
>
>     static char *kwlist[] = {"state", "action", "type","voltage", NULL};
>     if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|sss",kwlist,
>                                                 &voltage,&state,&action,&type))
>
>     printf("%s %i \n",action,voltage);
>     printf("%s %s \n",state,type);
>     Py_INCREF(Py_None);
>     return Py_None;
> }
>
>
> si en vez de utilizar el PyArg_ParseTupleAndKeywords, utilizo
> PyArg_ParseTuple de la siguiente manera:
>
>     if(!PyArg_ParseTuple(args,"i|sss",&voltage,&state,&action,&type))
>         return NULL;
>
> El resultado es el mismo...entonces no se por donde pasa la diferencia

El resultado es el mismo *sólo* si la llamada la haces con una tupla que  
contiene los argumentos en ese mismo orden. Trasladado mas o menos en  
Python, el primer caso es:

def funcion(*args, **kwargs):
   state = 'a'
   action = 'a'
   type = 'a'

   voltage = args.pop(0)
   assert not args, "argumento posicional inesperado: %r" % args
   # el if está porque es opcional
   if 'state' in kwargs: state = kwargs.pop('state')
   if 'action' in kwargs: action = kwargs.pop('action')
   if 'type' in kwargs: type = kwargs.pop('type')
   assert not kwargs, "argumento nominal inesperado: %r" % kwargs

y el segundo:

def funcion(*args):
   state = 'a'
   action = 'a'
   type = 'a'

   voltage = args.pop(0)
   # estos otros son opcionales
   if args: state = args.pop(0)
   if args: action = args.pop(0)
   if args: type = args.pop(0)
   assert not args, "argumento posicional inesperado: %r" % args

(con la diferencia de que esos pop() NO modifican la tupla/diccionario  
originales)

> de uso de estas dos funciones...y me queda otra duda cual es la mision
> de kwlist, tampoco la entiendo mucho..porque si en vez de poner los
> nombre de las variables y pongo cualquier cosa, el modulo cuando lo
> importo desde python funciona igualmente bien, la modificacion que
> realice a kwlist es la siguiente:
>
> static char *kwlist[] = {"algo", "algodos", "algotres","algocuatro",  
> NULL};

Lo que esta a la derecha de la | es opcional; asi que si tu formato era el  
mismo de arriba ("i|sss") entonces solo el primer argumento "algo" era  
obligatorio; si se lo pasaste en la tupla args entonces todos los demás  
eran opcionales y podian no existir. Ademas en el primer ejemplo veo que  
no estas verificando bien el codigo de retorno de PyArg_ParseXXX (que  
probablemente devolvio NULL) asi que si te dio error, no te enteraste.

-- 
Gabriel Genellina

------------ próxima parte ------------
_______________________________________________
Lista de correo Python-es 
http://listas.aditel.org/listinfo/python-es
FAQ: http://listas.aditel.org/faqpyes


Más información sobre la lista de distribución Python-es