Qué estilo usar para los parámetros de retorno no utilizados en una llamada a la función Python

30

¿Existe algún estilo de codificación recomendado / generalmente aceptado para manejar situaciones en las que una función devuelve una tupla de valores pero solo se usa uno de esos valores después (tenga en cuenta que esto está destinado principalmente a las funciones de biblioteca que no puedo cambiar: escribir un contenedor alrededor la llamada es probablemente un poco exagerada ...)? En lugar de hacer

a, b, c = foo()

y luego simplemente no usa by c, ¿cuál de las siguientes variantes debería preferirse (o hay otra?):

Variante 1 (guión bajo)

a, _, _ = foo()

(que es muy claro y simple, pero podría chocar con el _ = gettext.gettextutilizado en muchas aplicaciones que usan traducción)

Variante 2 (nombre ficticio)

a, unused, unused = foo()

(No muy atractivo, creo, lo mismo ocurre con otros nombres como dummy)

Variante 3 (índice)

a = foo()[0]

(para mí las ()[0]miradas no pitónicas ...)

usuario49643
fuente
En contraste con mi respuesta anterior, ¿qué sucede con la variante 3 cuando ahora desea hacer referencia a 2/3 de los valores de retorno? Eliminé mi respuesta porque me di cuenta de que no sabía lo suficiente sobre Python para justificarlo.
Craige
@Craige: No vi tu respuesta antes de que la quitaras ... ¿Estás preguntando si a, b = foo()[0:2]funcionaría? En caso afirmativo: sí, lo hace :)
user49643
Eso fue exactamente lo que estaba preguntando. Si eso funciona (como dijiste), usaría la variante 3. Estoy reinstalando mi respuesta dada esta información.
Craige
3
Por cierto, en estos días puede usar la sintaxis bastante hermosa para desempaquetar 'extendido' : a, *_ = foo()desechará todos los valores excepto el primero.
Benjamin Hodgson
duplicado de stackoverflow.com/questions/431866/…
Trevor Boyd Smith

Respuestas:

17

Usar el guión bajo para las variables no utilizadas es definitivamente aceptable. Sin embargo, tenga en cuenta que en algunas bases de código no es una opción, ya que ese identificador está reservado como abreviatura gettext. Esta es la objeción más frecuente a este estilo (aunque, por lo que puedo juzgar, no es un problema para la mayoría). Todavía lo recomiendo, y siempre lo uso yo mismo.

Los nombres me gustan dummyo unusedtienden a irritarme personalmente, y no los veo con demasiada frecuencia (en Python, es decir, conozco una base de código de Delphi que se usa dummygenerosamente, y también se ha filtrado en los scripts asociados con el programa en cuestión). Te aconsejaría que no lo hagas.

Simplemente extraer un artículo de la tupla devuelta también está bien. También ahorra algunos problemas al obtener la cantidad correcta de valores no utilizados. Sin embargo, tenga en cuenta que tiene dos desventajas potenciales:

  • No explota si la cantidad de valores es diferente de lo que espera. Esto puede ser útil para detectar confusiones y errores tipográficos.
  • Solo funciona cuando el valor de retorno es una secuencia (que es principalmente tuplas y listas, pero seamos generales). Por casualidad, conozco una clase en mi código (un vector 2D) que es iterable y produce un número constante de valores (y, por lo tanto, puede usarse en las tareas de desempaquetado), pero no es indexable.

fuente
Yo he hecho menciono getText en mi pregunta :) De todos modos, he aceptado su respuesta - parece que no hay un estilo único, aceptado clara, pero su respuesta ha planteado algunas cuestiones interesantes.
user49643
Para evitar problemas con gettext (o para evitar problemas similares en el intérprete) puede usar guiones bajos dobles (también conocidos como dunders) en lugar del sencillo.
Zim
21

Pylint me ha acostumbrado a hacerlo de esta manera:

widget, _parent, _children = f()

Es decir, los resultados no utilizados tienen un nombre descriptivo con el prefijo _. Pylint considera los locales con el prefijo _ como no utilizados, y los globales o atributos con el prefijo _ como privados.

Vincent Povirk
fuente
3

Si en todo su proyecto, solo está haciendo esto una vez o muy raramente con la función particular, usaría la Variante 1 si sabe gettextque no es un problema en ese módulo, y de lo contrario, la Variante 3.

Por otro lado, si estaba haciendo esto mucho, y especialmente si cada vez que desea un subconjunto diferente de los valores de retorno (haciendo que un contenedor solo devuelva los que le importan inviables), podría ser beneficioso escribir un contenedor eso pone los resultados en una tupla con nombre , o una instancia de alguna otra clase descriptiva, que le permitiría hacer:

bar = foo()

Y luego trabajar con bar.a, bar.by bar.c.

lvc
fuente
2

Como otros han dicho, subrayar ( _) es el estándar. Pero si el subrayado se usa para las traducciones, creo que el doble subrayado es la mejor alternativa.

var, __, value = "VAR=value".partition('=')

Mejor que estos:

var, unused, value = "VAR=value".partition('=')

var, unused_del, value = "VAR=value".partition('=')

var, _del, value = "VAR=value".partition('=')

Rick Mohr
fuente
1

Soy un programador que no es Python, pero para mí la tercera variante tiene más sentido.

En la variante 3, tiene absolutamente claro qué valores le interesa tratar. En las variantes 1 y 2, está asignando los valores a las variables y, por lo tanto, se pueden usar. Es posible que los haya nombrado oscuramente, pero una mala denominación no es realmente una solución a ningún problema.

Además de la claridad, ¿por qué desearía asignar valores no utilizados a una ranura en la memoria (como en las variantes 1 y 2)? Esta sería una solución pobre en términos de administración de memoria.

Craige
fuente
Si siempre lo pones en la misma variable, ¿no sería el problema de memoria del tamaño de una sola variable en un momento dado? No creo que eso deba ser un problema.
Michael McQuade
-2

Aquí hay una regla general: si solo se usa 1 de los valores devueltos, ¿por qué no simplemente devolver ese 1 valor? En caso de que se llame a la bandera desde varios lugares, mantenga una bandera para el mismo y devuelva los valores según la bandera.

EDITAR:

En caso de que estuviera en su situación y no hubiera escrito la función, tal vez optaría por la Variante 2. Mantendría los nombres ficticios allí para que los parámetros se puedan usar en caso de necesidad. Cortar una lista tendrá un poco de sobrecarga. Quizás pueda ahorrar algunos bytes para recibir los datos no deseados.

c0da
fuente
-1 Supongo que no definió la función y, por lo tanto, no puede cambiar lo que devuelve. Creo que se refiere a cuando la función devuelve 3 valores, pero solo le importa 1 en su caso de uso actual.
Craige
@Craige: Sí, me refería a funciones que no podía cambiar; agregué una nota a mi pregunta para aclarar eso.
user49643
¿Estás seguro de los gastos generales? Intenté una prueba de ipython muy simple: %timeit a, _, _ = foo()vs. %timeit a = foo()[0]que resultó en 123ns vs. 109ns, es decir, el corte parece ser realmente más rápido que el desempaquetado de valor. ¿O tenía alguna situación específica en mente?
user49643
2
Otro escenario plausible es que no necesita todos los valores para algunas llamadas.
Keith Thompson