[pyar] función con valores de retorno distintos

Daniel Moisset dmoisset en machinalis.com
Mar Jul 29 15:19:44 ART 2014


PD: Lo de poner "pazas" es nvez de "pasás" es para infartar un poco a los
grammar nạzis.


2014-07-29 15:14 GMT-03:00 Daniel Moisset <dmoisset en machinalis.com>:

> Mirá, el tema es complicado, y como todos los temas complicados la
> respuesta a la pregunta es "depende". Depende esencialmente de que la
> funcion sea comoda de usar para el que la invoca.
>
> Por un lado, la idea de "tipos distintos" en python es difusa. Alguna
> funcion puede estar devolviendo a veces un generator object, un generator
> expression, o un iterador de listas, y probablemente nadie se da cuenta,
> porque todo lo que hacen afuera es iterarlo. Entonces si bien son tipos
> distintos (en el sentido de que type(f(x)) da distintos valores), son el
> mismo tipo en el sentido "abstracto" de tipado (tienen tipo "cosas que se
> pueden iterar"). Ese tipo de variación es super normal. No solo con listas,
> sino con cualquieras dos cosas que tengan interfaces parecidas.
>
> En este caso hay que tener cuidado con lo que documentas que se devuelvo.
> Una funcion que a veces devuelve un iterador, y otras veces una lista sin
> un criterio claro, corre el riesgo de que el llamador ponga x =
> mifuncion()[17], y a veces funcione y otras no.
>
> si el criterio esta claro no jode. por ej. si la funcion tiene un
> argumento "iterable" que cuando le pasas False te garantiza que te da una
> lista, esta todo ok.
>
> A veces los tipos de retorno posibles no tienen nada que ver, pero estan
> claros para el llamador. Fiajte por ejemplo el builtin max de python:
>
> max(1, 36, -7) == 36
> max("pan", "salame", "queso") == "salame"
>
> si bien cadenas y enteros son tipos muy incompatibles, no hay ninguna
> confusion desde el codigo de que esperar en el resultado (en los casos
> "normales", en python 2 podes poner max('x','y',37) y da algun resultado).
> En este ejemplo tenes una variacion de tipos mas similar a genericidad que
> a multiples implementaciones de una interfaz.
>
> Con las cantidades es parecido. Si tenes una funcion que se puede usar
>
> value = get_some_value(...)
> value, metadata = get_some_value(..., with_metadata=True)
>
> es bastante razonable. Me parece que es parecido a tu caso, vos sabes
> cuantos header_lines (adivino que usualmente es una constante en la
> llamada), aunque tu caso tiene algunas particularidades.
>
> En general, si tenes un if "innecesario" despues de la llamada para
> enterarte de que carancho devolvió, puede que estes en uno de los casos
> "malos". Si en tu ejemplo, header_lines tipicamente viene de un archivo de
> configuracion, va s a tener que hacer
>
> header, *lines = _get_lines(infile, config.header_lines)
> if header is None: ...
> elif isinstance(header, list): ...
> else: ...
>
> Fijate que acá ya se pone bastante "turbio" el codigo del llamador. Sobre
> todo porque es medio dificil distinguir entre una lista y una cadena
> (tienen interfaces muy parecidas). Pero todo depende, recien puse el
> "innecesario" a lado del if, porque una funcion puede devolver o bien un
> objeto o bien None en situaciones bien distintas, y ahi vas a tener dos
> casos que por la naturaleza del problema conviene manejar distinto. Igual
> si encontras una generalización te suele acabar ahorrando trabajo
>
> El resumen es: los tipos pueden variar, las cantidades pueden variar, pero
> usar la funcion tiene que ser "comodo"
>
> Volviendo a tu ejemplo, yo capaz hubiera hecho:
>
>
> return ret[:header_lines], ret[header_lines:]
>
> y luego de eso lo uso así:
>
> _, lines = _get_lines(infile, header_lines=0)
> [header], lines = _get_lines(infile, header_lines=1)
> [header1, header2], lines = _get_lines(infile, header_lines=2)
>
> Y en el caso general
>
> headers, lines = _get_lines(infile, header_lines=expresion que sale de
> algun lado desconocido)
> for h in headers: ...
>
> Pero estoy adivinando, y todo depende no de la funcion sino del lugar
> donde se usa. Si es un problema superaacotado y vos estas segurisisisisimo
> de que a header_lines= le pazas siempre una constante, tu approach esta
> barbaro.
>
> Saludos,
>     D.
>
>
>
>
> 2014-07-29 14:33 GMT-03:00 Matias Graña <matias.alejo en gmail.com>:
>
> Gracias!
>> Pensé en algo así y está lindo, pero todo el programa es cortito y creo
>> que no vale la pena extenderlo con nuevas clases. Esta es para mí LA
>> solución en algo más complejo.
>>
>> El punto central para mí es: es buena o mala práctica tener un método
>> cuyos valores de retorno cambian (cambia la cantidad y/o los tipos) en
>> función de los parámetros?
>>
>> -- Matías Graña
>>
>>
>> 2014-07-29 11:39 GMT-03:00 Andres Riancho <andres.riancho en gmail.com>:
>>
>> Capaz podes meter todo dentro de un objeto "Linea", devolver siempre
>>> Linea(s) y sobre ellas hacer:
>>>
>>> linea.get_header() => None, lista, string
>>> linea.get_header_type() => Alguno de una lista de [NO_HEADER,
>>> HEADER_LIST, HEADER_STRING]
>>>
>>> Y despues podrías en tu codigo tener algo que en base al tipo y al
>>> header lo procese.
>>>
>>> FMAP = {NO_HEADER: funcion_a,
>>>               HEADER_LIST: funcion_b,
>>>               HEADER_STRING: funcion_c}
>>>
>>> def procesa_lineas(linea):
>>>     return FMAP[linea.get_header_type()](linea)
>>>
>>>
>>> 2014-07-29 11:28 GMT-03:00 Matias Graña <matias.alejo en gmail.com>:
>>> > Hola;
>>> > tengo una función que lee un archivo, hace alguna cosa y devuelve el
>>> > contenido separado por líneas y, eventualmente, separando una línea (o
>>> más)
>>> > de headers.
>>> > Se puede hacer así (versión simplificada):
>>> >
>>> > def _get_lines(infile, header_lines=1):
>>> >     with open(infile, 'r') as data:
>>> >         ret = [blabla(line) for line in data]
>>> >     header = None if not header_lines else ret[0] if header_lines == 1
>>> else
>>> > ret[:header_lines]
>>> >     return header, ret[header_lines:]
>>> >
>>> > Eso se llama con
>>> > header, lines = _get_lines(infile)
>>> > El punto es que header puede ser None, un str o una lista de str, en
>>> función
>>> > del valor de header_lines. La pregunta es (y es más amplia que este
>>> ejemplo
>>> > puntual) si es razonable que los valores de retorno de una función
>>> cambien
>>> > el tipo de esta manera.
>>> > Esta forma de hacerlo tiene la ventaja de que los casos de uso típico
>>> van a
>>> > ser con header_lines = 0 o 1 y entonces en esos casos es cómodo. Pero
>>> si en
>>> > algún momento cambia algo y header_lines debe ser > 1, hay que cambiar
>>> el
>>> > código un poco más que si en el return tuviera
>>> >
>>> > return ret[:header_lines], ret[header_lines:]
>>> >
>>> > Otra opción, que tiene sus pros y sus contras también, es
>>> >
>>> > return tuple(ret[:header_lines]) + (ret[header_lines:],)
>>> >
>>> > Eso hace que deba llamarse con
>>> >
>>> > lines = _get_lines(infile, header_lines=0)
>>> > header, lines = _get_lines(infile, header_lines=1)
>>> > header1, header2, lines = _get_lines(infile, header_lines=2)
>>> >
>>> > etc. Acá lo que cambia es la cantidad de datos a devolver, y también
>>> puede
>>> > ser propenso a errores. En python3 sería un poco más sencillo, porque
>>> puedo
>>> > devolver directamente tuple(ret), sin calentarme por header_lines, y
>>> obligar
>>> > al llamado a que sea del estilo de
>>> >
>>> > header, *lines = _get_lines(infile)
>>> >
>>> > Lamentablemente, python3 no es una posibilidad acá. Imagino que no hay
>>> una
>>> > única manera correcta de hacer las cosas, pero argumentos a favor o
>>> encontra
>>> > de estas opciones son bienvenidos.
>>> >
>>> > Gracias!
>>> >
>>> > -- Matías Graña
>>> >
>>> > _______________________________________________
>>> > pyar mailing list pyar en python.org.ar
>>> > http://listas.python.org.ar/listinfo/pyar
>>> >
>>> > PyAr - Python Argentina - Sitio web: http://www.python.org.ar/
>>> >
>>> > La lista de PyAr esta Hosteada en USLA - Usuarios de Software Libre de
>>> > Argentina - http://www.usla.org.ar
>>>
>>>
>>>
>>> --
>>> Andrés Riancho
>>> Project Leader at w3af - http://w3af.org/
>>> Web Application Attack and Audit Framework
>>> Twitter: @w3af
>>> GPG: 0x93C344F3
>>> _______________________________________________
>>> pyar mailing list pyar en python.org.ar
>>> http://listas.python.org.ar/listinfo/pyar
>>>
>>> PyAr - Python Argentina - Sitio web: http://www.python.org.ar/
>>>
>>> La lista de PyAr esta Hosteada en USLA - Usuarios de Software Libre de
>>> Argentina - http://www.usla.org.ar
>>>
>>
>>
>> _______________________________________________
>> pyar mailing list pyar en python.org.ar
>> http://listas.python.org.ar/listinfo/pyar
>>
>> PyAr - Python Argentina - Sitio web: http://www.python.org.ar/
>>
>> La lista de PyAr esta Hosteada en USLA - Usuarios de Software Libre de
>> Argentina - http://www.usla.org.ar
>>
>
>
------------ próxima parte ------------
Se ha borrado un adjunto en formato HTML...
URL: <http://listas.python.org.ar/pipermail/pyar/attachments/20140729/c8f6e83f/attachment-0001.html>


More information about the pyar mailing list