Qual estilo usar para parâmetros de retorno não utilizados em uma chamada de função Python

30

Existe algum estilo de codificação recomendado / geralmente aceito para lidar com situações em que uma função retorna uma tupla de valores, mas apenas um desses valores é usado posteriormente (observe que isso se destina principalmente a funções de biblioteca que não posso alterar - escrevendo um wrapper a ligação provavelmente é um pouco exagerada ...)? Em vez de fazer

a, b, c = foo()

e, em seguida, apenas não usando be c, qual das seguintes variantes deve ser preferida (ou existe outra?):

Variante 1 (sublinhado)

a, _, _ = foo()

(que é muito claro e simples, mas pode colidir com o _ = gettext.gettextusado em muitos aplicativos que usam tradução)

Variante 2 (nome fictício)

a, unused, unused = foo()

(acho que não muito atraente, o mesmo vale para outros nomes como dummy)

Variante 3 (índice)

a = foo()[0]

(para mim os ()[0]olhares não pitônicos…)

user49643
fonte
Em contraste com a minha resposta anterior, o que acontece com a variante 3 quando você deseja agora referenciar 2/3 dos valores de retorno? Tirei minha resposta porque percebi que não sabia o suficiente sobre Python para justificá-la.
Craige
@ Craig: Eu não vi sua resposta antes de removê-la ... Você está perguntando se a, b = foo()[0:2]funcionaria? Se sim: sim, ele faz :) #
49493 user49643 13/0312
Era exatamente o que eu estava perguntando. Se isso funcionar (como você disse), eu usaria a variante 3. Estou restabelecendo minha resposta, com essas informações.
Craige
3
Aliás, hoje em dia você pode usar a sintaxe bastante bonita para descompactar 'estendida' : a, *_ = foo()jogará fora todos os valores, exceto o primeiro.
Benjamin Hodgson
duplicado de stackoverflow.com/questions/431866/…
Trevor Boyd Smith

Respostas:

17

Usar o sublinhado para variáveis ​​não utilizadas é definitivamente aceitável. Porém, esteja avisado que, em algumas bases de código, não é uma opção, pois esse identificador está reservado como abreviação para gettext. Essa é a objeção mais frequente a esse estilo (embora não seja um problema para a maioria, tanto quanto eu possa julgar). Eu ainda recomendo e sempre o uso sozinho.

Os nomes gostam dummyou unusedtendem a me irritar pessoalmente, e eu não os vejo muito frequentemente (em Python, isto é - eu conheço uma base de código Delphi que usa dummyliberalmente, e também vazou para scripts associados ao programa em questão). Eu o aconselharia contra.

Também é bom extrair um item da tupla retornada. Ele também evita alguns problemas ao acertar o número de valores não utilizados. Observe que ele tem duas desvantagens em potencial:

  • Não explode se o número de valores for diferente do que você espera. Isso pode ser útil para detectar misturas e erros de digitação.
  • Funciona apenas quando o valor de retorno é uma sequência (que é principalmente tuplas e listas, mas vamos permanecer gerais). De antemão, conheço uma classe no meu código (um vetor 2D) que é iterável e gera um número constante de valores (e, portanto, pode ser usado para descompactar atribuições), mas não é indexável.

fonte
Eu tinha realmente mencionar gettext na minha pergunta :) Enfim, eu aceito a sua resposta - parece que não existe um estilo único aceite clara, mas a sua resposta levantou alguns pontos interessantes.
user49643
Para evitar problemas com o gettext (ou evitar problemas semelhantes no intérprete), você pode usar sublinhados duplos (aka dunders) em vez do single.
Zim
21

Pylint me adquiriu o hábito de fazê-lo desta maneira:

widget, _parent, _children = f()

Ou seja, resultados não utilizados têm um nome descritivo prefixado por _. O Pylint considera os locais prefixados com _ como não utilizados e os globais ou atributos prefixados com _ como privados.

Vincent Povirk
fonte
3

Se em todo o seu projeto, você estiver fazendo isso apenas uma vez ou muito raramente na função específica, eu usaria a variante 1 se você gettextnão souber que há um problema nesse módulo e, caso contrário, a variante 3.

Por outro lado, se você estava fazendo muito isso - e especialmente se toda vez que quiser um subconjunto diferente dos valores de retorno (criar um invólucro para retornar apenas aqueles inviáveis), pode ser benéfico escrever um invólucro que coloca os resultados em uma tupla nomeada ou em uma instância de outra classe descritiva, o que permitiria:

bar = foo()

E então trabalhe com bar.a, bar.be bar.c.

lvc
fonte
2

Como outros já disseram, underscore ( _) é o padrão. Mas se o sublinhado estiver sendo usado para traduções, acho que o sublinhado duplo é a melhor alternativa.

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

Melhor que estes:

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

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

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

Rick Mohr
fonte
1

Eu não sou um programador Python, mas para mim a terceira variante faz mais sentido.

Na variante 3, você é absolutamente claro com quais valores você está interessado em lidar. Nas variantes 1 e 2, você está atribuindo os valores de volta às variáveis ​​e, portanto, elas podem ser usadas. Você pode tê-los nomeado obscuramente, mas a má nomeação não é realmente uma solução para nenhum problema.

Além da clareza, por que você deseja atribuir valores não utilizados a um slot na memória (como nas variantes 1 e 2)? Essa seria uma solução ruim em termos de gerenciamento de memória.

Craige
fonte
Se você sempre colocá-lo na mesma variável, o problema de memória não seria apenas do tamanho de uma única variável a qualquer momento? Eu não acho que isso seja um problema.
22717 Michael McQuade
-2

Aqui está uma regra geral: se apenas 1 dos valores retornados for usado, por que não simplesmente retornar esse 1 valor? Caso o sinalizador esteja sendo chamado de vários locais, mantenha um sinalizador para o mesmo e retorne valores conforme o sinalizador.

EDITAR:

No caso de você estar na sua situação e não ter escrito a função, talvez eu vá para a Variante 2. Eu manteria os nomes fictícios lá para que os parâmetros possam ser usados ​​em caso de necessidade. Cortar uma lista terá um pouco de sobrecarga. Talvez você possa poupar alguns bytes para receber os dados indesejados.

c0da
fonte
-1 Acho que ele não definiu a função e, portanto, não pode alterar o que ela retorna. Eu acho que ele está se referindo a quando a função retorna 3 valores, mas ele se importa apenas com 1 no seu caso de uso atual.
Craige
@ Craig: Sim, eu quis dizer funções que não pude alterar - adicionei uma nota à minha pergunta esclarecendo isso.
user49643
Você tem certeza da sobrecarga? Eu tentei um teste ipython muito simples: %timeit a, _, _ = foo()vs. o %timeit a = foo()[0]que resultou em 123ns vs. 109ns, ou seja, o fatiamento parece ser realmente mais rápido que o valor descompactado. Ou você tinha alguma situação específica em mente?
user49643
2
Outro cenário plausível é que você não precisa de todos os valores para algumas chamadas.
Keith Thompson