Maneira mais elegante de declarar várias variáveis ​​ao mesmo tempo

140

Para declarar várias variáveis ​​ao mesmo tempo, eu faria:

a, b = True, False

Mas se eu tivesse que declarar muito mais variáveis, isso se tornaria cada vez menos elegante:

a, b, c, d, e, f, g, h, i, j = True, True, True, True, True, False, True ,True , True, True

Existe uma maneira melhor / elegante / conveniente de fazer isso?

Isso deve ser muito básico, mas se eu usasse uma lista ou uma tupla para armazenar as variáveis, como teria que me aproximar para ser útil, pois:

aList = [a,b]

Não é válido, eu teria que fazer:

a, b = True, True

Ou o que estou perdendo?

Trufa
fonte
Use uma lista para armazenar esses valores? Um dicionário? Uma tupla (nomeada)?
Jeff Mercado
@ Chris: eu estava chegando lá. :)
Jeff Mercado
@JeffM: talvez, mas eu não sei como fazer isso parece que eles têm que ser definidos, a fim de pertencer a uma lista (posso estar errado, claro)
Trufa
3
@ Trufa: Se você vai declarar muitas variáveis ​​para armazenar valores, isso já é um sinal de que você deve considerar outras alternativas de armazenamento IMHO.
Jeff Mercado
1
@ user470379 - Eu meio que assumi que os nomes eram apenas para o código de exemplo e que Trufa não estava usando esses nomes em seu código real.
Chris Lutz

Respostas:

52

Como outros sugeriram, é improvável que o uso de 10 variáveis ​​locais diferentes com valores booleanos seja a melhor maneira de escrever sua rotina (especialmente se eles realmente tiverem nomes de uma letra :)

Dependendo do que você está fazendo, pode fazer sentido usar um dicionário. Por exemplo, se você deseja configurar valores predefinidos booleanos para um conjunto de sinalizadores de uma letra, você pode fazer o seguinte:

>>> flags = dict.fromkeys(["a", "b", "c"], True)
>>> flags.update(dict.fromkeys(["d", "e"], False))
>>> print flags
{'a': True, 'c': True, 'b': True, 'e': False, 'd': False}

Se preferir, você também pode fazê-lo com uma única declaração de atribuição:

>>> flags = dict(dict.fromkeys(["a", "b", "c"], True),
...              **dict.fromkeys(["d", "e"], False))
>>> print flags
{'a': True, 'c': True, 'b': True, 'e': False, 'd': False}

O segundo parâmetro para dictnão foi totalmente projetado para isso: ele realmente permite que você substitua elementos individuais do dicionário usando argumentos de palavras-chave como d=False. O código acima explode o resultado da expressão a seguir **em um conjunto de argumentos de palavras-chave que são passados ​​para a função chamada. Essa é certamente uma maneira confiável de criar dicionários, e as pessoas parecem aceitar pelo menos esse idioma, mas suspeito que alguns possam considerá-lo não-atônico. </disclaimer>


Ainda outra abordagem, que provavelmente é a mais intuitiva se você usar esse padrão com freqüência, é definir seus dados como uma lista de valores de sinalizadores ( True, False) mapeados para nomes de sinalizadores (cadeias de caracteres únicos). Em seguida, você transforma essa definição de dados em um dicionário invertido que mapeia nomes de sinalizadores para valores de sinalizador. Isso pode ser feito de maneira bastante sucinta com uma compreensão de lista aninhada, mas aqui está uma implementação muito legível:

>>> def invert_dict(inverted_dict):
...     elements = inverted_dict.iteritems()
...     for flag_value, flag_names in elements:
...         for flag_name in flag_names:
...             yield flag_name, flag_value
... 
>>> flags = {True: ["a", "b", "c"], False: ["d", "e"]}
>>> flags = dict(invert_dict(flags))
>>> print flags
{'a': True, 'c': True, 'b': True, 'e': False, 'd': False}

A função invert_dicté uma função geradora . Ele gera ou produz - o que significa que retorna repetidamente valores de - pares de chave-valor. Esses pares de valores-chave são o inverso do conteúdo dos dois elementos do flagsdicionário inicial . Eles são alimentados no dictconstrutor. Nesse caso, o dictconstrutor trabalha de maneira diferente por cima, porque está sendo alimentado por um iterador em vez de um dicionário como argumento.


Aproveitando o comentário de @Chris Lutz: se você realmente usar isso para valores de um caractere, poderá fazer

>>> flags = {True: 'abc', False: 'de'}
>>> flags = dict(invert_dict(flags))
>>> print flags
{'a': True, 'c': True, 'b': True, 'e': False, 'd': False}

Isso funciona porque as seqüências de caracteres Python são iteráveis , o que significa que elas podem ser movidas através de valor por valor. No caso de uma sequência, os valores são os caracteres individuais na sequência. Assim, quando eles estão a ser interpretado como iterables, como no presente caso onde estão a ser utilizados em um loop, ['a', 'b', 'c']e 'abc'são efectivamente equivalentes. Outro exemplo seria quando eles estão sendo passados ​​para uma função que requer um iterável, como tuple.

Eu pessoalmente não faria isso porque não lê intuitivamente: quando vejo uma string, espero que seja usada como um valor único e não como uma lista. Então, olho para a primeira linha e penso "Ok, então há uma bandeira True e uma bandeira False". Portanto, embora seja uma possibilidade, não acho que seja o caminho a seguir. Em contrapartida, pode ajudar a explicar os conceitos de iteráveis ​​e iteradores mais claramente.


Definindo a função invert_dict forma que ela realmente retorne um dicionário também não é uma má idéia; Na maioria das vezes, não fiz isso porque realmente não ajuda a explicar como a rotina funciona.


Aparentemente, o Python 2.7 possui compreensão de dicionário, o que tornaria uma maneira extremamente concisa de implementar essa função. Isso é deixado como um exercício para o leitor, pois não tenho o Python 2.7 instalado :)

Você também pode combinar algumas funções do módulo de ferramentas sempre versátil . Como se costuma dizer, há mais de uma maneira de fazer isso . Espere, o pessoal do Python não diz isso. Bem, é verdade mesmo assim em alguns casos. Eu acho que Guido nos deu uma compreensão do dicionário para que houvesse uma maneira óbvia de fazer isso.

intuído
fonte
1
Note que ['a', 'b', 'c']pode ser encurtado para list('abc'), o que inspira def truth_values(trues, falses): d = dict.from_keys(list(trues), True); d.update(dict.from_keys(list(falses), False)); return dusado comovalues = truth_values("abc", "de")
Chris Lutz
Muito obrigado, esta parece ser uma resposta muito abrangente. Vou dar uma boa olhada e experimentar o que você escreve, o que você está dizendo, embora provavelmente seja verdade, não é natural para maio, então terei que leia um pouco e brinque até entender completamente o que você quer dizer, especialmente porque os dicionários ainda são uma das minhas fraquezas em python. Muito obrigado, eu vou estar de volta :)
Trufa
5
@intuited Estou impressionado com a sua resposta: você define outro problema que não o do OP e gosta de fazer uma longa resposta a esse outro problema. Ele não deseja associar seqüências de caracteres e valores em um dicionário, ele deseja criar objetos com um identificador e um valor para cada um.
Eyquem
5
@eyquem: Respostas longas são ruins? Médico, cure-se!
precisa
5
Isso não responde à pergunta e é condescendente. Veja a resposta abaixo
kodu
225
a, b, c, d, e, g, h, i, j = (True,)*9
f = False
Shariq
fonte
21
@Imray A notação de vírgula à direita (d,)cria uma tupla de um item, que é um tipo de sequência. Os tipos de sequência suportam adição e multiplicação, entre outras operações.
Duozmo 27/05
36
Truque elegante, mas esteja ciente de usá-lo em elementos mutáveis, como listas. Por exemplo a, b, c = ([],)*3não cria 3 instâncias lista, mas marcas a, be capontar para a mesma instância.
Zac
5
Gostaria de saber quanto tempo eu gasto / gasto tentando fazer meu código python uber pythonic?
SARose
3
@ Zac para fazer isso corretamente a, b, c = ([] for i in range(3)). fonte . Para obter consistência, você também pode usar uma variante disso para esta resposta, ou seja a,b,c,d,e,g,h,i,j = (True for i in range(9)) f=(False i in range(1)),.
Novato C
Este é um exemplo do motivo pelo qual eu amo python, apenas usando-o recentemente. Desejo que seja iniciado mais cedo. Mas o desenvolvimento da Web varia de acordo com o projeto.
irritada 84
52

Use uma lista / dicionário ou defina sua própria classe para encapsular o que você está definindo, mas se você precisar de todas essas variáveis, poderá:

a = b = c = d = e = g = h = i = j = True
f = False
yan
fonte
1
vars não serão configurados apenas como Verdadeiro e Falso.
N 1.1
1
@ N1.1: Eu não conseguia entender o que você quis dizer com isso.
Trufa 31/03
@ Trufa Se eles estiverem definidos como Verdadeiro / Falso, a maneira sugerida é perfeita, mas e se as variáveis ​​forem definidas para coisas diferentes?
N 1.1
@ N1.1: Ohh, entendi o que você quer dizer, obrigado pelo esclarecimento! Nesse caso, eles são todos bools, mas é bom saber. Graças
Trufa
5
Para uma explicação de por que isso é um padrão perigoso (embora exemplo de trabalho), ler sobre Notorious BIG
duozmo
10

Esta é uma elaboração sobre @ Jeff M 'e meus comentários.

Quando você faz isso:

a, b = c, d

Funciona com embalagem e descompactação de tupla. Você pode separar as etapas de embalagem e desembalagem:

_ = c, d
a, b = _

A primeira linha cria uma tupla chamada _que possui dois elementos, o primeiro com o valor de ce o segundo com o valor de d. A segunda linha descompacta a _tupla nas variáveis ae b. Isso divide sua única linha enorme:

a, b, c, d, e, f, g, h, i, j = True, True, True, True, True, False, True, True, True, True

Em duas linhas menores:

_ = True, True, True, True, True, False, True, True, True, True
a, b, c, d, e, f, g, h, i, j = _

Ele fornecerá exatamente o mesmo resultado da primeira linha (incluindo a mesma exceção se você adicionar valores ou variáveis ​​a uma parte, mas esquecer de atualizar a outra). No entanto, neste caso específico, a resposta de yan é talvez a melhor.

Se você tiver uma lista de valores, ainda poderá descompactá-los. Você apenas precisa convertê-lo em uma tupla primeiro. Por exemplo, o seguinte irá atribuir um valor entre 0 e 9, para cada uma apor meio de j, respectivamente:

a, b, c, d, e, f, g, h, i, j = tuple(range(10))

Edição: truque puro para atribuir todos eles como verdadeiro, exceto o elemento 5 (variável f):

a, b, c, d, e, f, g, h, i, j = tuple(x != 5 for x in range(10))
Chris Lutz
fonte
Eu não sabia que o último era possível, com certeza vou tentar, é realmente um truque legal e pode ser útil!
Trufa
5

Quando as pessoas estão sugerindo "usar uma lista ou tupla ou outra estrutura de dados", o que elas estão dizendo é que, quando você tem muitos valores diferentes com os quais se importa, nomeá-los todos separadamente como variáveis ​​locais pode não ser o melhor caminho. fazer coisas.

Em vez disso, convém reuni-los em uma estrutura de dados maior que possa ser armazenada em uma única variável local.

intuited mostrou como você pode usar um dicionário para isso, e Chris Lutz mostrou como usar uma tupla para armazenamento temporário antes de descompactar em variáveis ​​separadas, mas outra opção a considerar é usar collections.namedtuplepara agrupar os valores mais permanentemente.

Então você pode fazer algo como:

# Define the attributes of our named tuple
from collections import namedtuple
DataHolder = namedtuple("DataHolder", "a b c d e f g")

# Store our data
data = DataHolder(True, True, True, True, True, False, True)

# Retrieve our data
print(data)
print(data.a, data.f)

Espera-se que o código real use nomes mais significativos do que "DataHolder" e as letras do alfabeto, é claro.

ncoghlan
fonte
Obrigado pela sua resposta, vou verificar isso como uma opção, o fato é que ( para este caso em particular ) pode não ser útil ter uma estrutura imutável. Vou comentar mais tarde como isso acabou, mais uma vez muito obrigado!
Trufa
Você pode fazer o mesmo com uma classe comum - a definição de DataHolderapenas fica um pouco mais detalhada.
Ncoghlan 31/03
4

Qual é o problema, de fato?

Se você realmente precisa ou quer 10 a , b , c , d , e , f , g , h , i , j , não haverá outra possibilidade, em um momento ou outro, para escrever um e gravação b e escrever c . ....

Se os valores forem todos diferentes, você será obrigado a escrever por exemplo

a = 12
b= 'sun'
c = A() #(where A is a class)
d = range(1,102,5)
e = (line in filehandler if line.rstrip())
f = 0,12358
g = True
h = random.choice
i = re.compile('^(!=  ab).+?<span>')
j = [78,89,90,0]

isto é, definir as "variáveis" individualmente.

Ou, usando outra escrita, não é necessário usar _:

a,b,c,d,e,f,g,h,i,j =\
12,'sun',A(),range(1,102,5),\
(line for line in filehandler if line.rstrip()),\
0.12358,True,random.choice,\
re.compile('^(!=  ab).+?<span>'),[78,89,90,0]

ou

a,b,c,d,e,f,g,h,i,j =\
(12,'sun',A(),range(1,102,5),
 (line for line in filehandler if line.rstrip()),
 0.12358,True,random.choice,
 re.compile('^(!=  ab).+?<span>'),[78,89,90,0])

.

Se alguns deles devem ter o mesmo valor, é o problema que é muito longo para escrever

a, b, c, d, e, f, g, h, i, j = True, True, True, True, True, False, True ,True , True, True 

?

Então você pode escrever:

a=b=c=d=e=g=h=i=k=j=True
f = False

.

Não entendo qual é exatamente o seu problema. Se você deseja escrever um código, é obrigado a usar os caracteres exigidos pela escrita das instruções e definições. O quê mais ?

Gostaria de saber se sua pergunta não é o sinal de que você entendeu mal alguma coisa.

Quando se escreve a = 10, não se cria uma variável no sentido de "pedaço de memória cujo valor pode mudar". Esta instrução:

  • aciona a criação de um objeto do tipo integere valor 10 e a ligação de um nome 'a' com esse objeto no espaço para nome atual

  • ou redesigne o nome 'a' no espaço para nome ao objeto 10 (porque 'a' foi ligado anteriormente a outro objeto)

Digo isso porque não vejo o utilitário para definir 10 identificadores a, b, c ... apontando para Falso ou Verdadeiro. Se esses valores não mudam durante a execução, por que 10 identificadores? E se eles mudam, por que definir os identificadores primeiro?, Eles serão criados quando necessário, se não definidos anteriormente

Sua pergunta me parece estranha

eyquem
fonte
2

Parece que você está abordando o seu problema da maneira errada para mim.

Reescreva seu código para usar uma tupla ou escreva uma classe para armazenar todos os dados.

richo
fonte
Obrigado por você. Você pode dizer que sem nem um vislumbre do código :) Entendo que isso não é o ideal, e talvez seja necessário reformatar, mas sua resposta não resolve a questão. Eu entendo o seu ponto.
Trufa 31/03
1
Posso dizer que soa assim, sim. É o que parece. A refatoração provavelmente resolverá seu problema. Seu problema é um problema de estilo, não funcional, por isso é difícil oferecer outra coisa que não seja um metadício.
richo 31/03
1

Eu gosto da resposta mais votada; no entanto, ele tem problemas com a lista, como mostrado.

  >> a, b = ([0]*5,)*2
  >> print b
  [0, 0, 0, 0, 0]
  >> a[0] = 1
  >> print b
  [1, 0, 0, 0, 0]

Isso é discutido em grandes detalhes (aqui) , mas a essência é isso ae bé o mesmo objeto com a is bretorno True(o mesmo para id(a) == id(b)). Portanto, se você alterar um índice, estará alterando o índice de ambos ae b, uma vez que eles estão vinculados. Para resolver isso, você pode fazer (fonte)

>> a, b = ([0]*5 for i in range(2))
>> print b
[0, 0, 0, 0, 0]
>> a[0] = 1
>> print b
[0, 0, 0, 0, 0]

Isso pode ser usado como uma variante da resposta principal, que possui os resultados intuitivos "desejados"

>> a, b, c, d, e, g, h, i = (True for i in range(9))
>> f = (False for i in range(1)) #to be pedantic
Iniciante C
fonte
1

No seu caso, eu usaria o YAML.

Esse é um padrão elegante e profissional para lidar com vários parâmetros. Os valores são carregados de um arquivo separado. Você pode ver algumas informações neste link:

https://keleshev.com/yaml-quick-introduction

Mas é mais fácil pesquisar no Google, por ser um padrão, existem centenas de informações sobre ele, você pode encontrar o que melhor se adapta à sua compreensão. ;)

Cumprimentos.

Henrique LR
fonte
Olá Henrique, bem-vindo ao SO, obrigado pela resposta! Certifique-se de ler a resposta antes de responder à sua próxima pergunta!
Diggy.
0

Como o JavaScript, você também pode usar várias instruções em uma linha em pythona = 1; b = "Hello World"; c += 3

Isaac Frost
fonte