Un modo più elegante di dichiarare più variabili contemporaneamente


140

Per dichiarare più variabili contemporaneamente "farei:

a, b = True, False

Ma se dovessi dichiarare molte più variabili, diventerebbe sempre meno elegante:

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

Esiste un modo migliore / elegante / conveniente per farlo?

Questo deve essere molto semplice, ma se uso una lista o una tupla per memorizzare le variabili, come dovrei approcciarmi in modo da essere utile dal momento che:

aList = [a,b]

Non è valido, dovrei fare:

a, b = True, True

O cosa mi sto perdendo?


Utilizzare un elenco per memorizzare quei valori? Un dizionario? Una tupla (nominata)?
Jeff Mercado,

@ Chris: ci stavo arrivando. :)
Jeff Mercado,

@JeffM: forse ma non so come farlo sembra che debbano essere definiti per appartenere a un elenco (potrei sbagliarmi ovviamente)
Trufa

3
@Trufa: Se stai per dichiarare che molte variabili memorizzano valori, questo è già un segno che dovresti prendere in considerazione altre alternative di archiviazione IMHO.
Jeff Mercado,

1
@ user470379 - Ho ipotizzato che i nomi fossero solo per il codice di esempio e che Trufa non stesse usando quei nomi nel suo codice reale.
Chris Lutz,

Risposte:


52

Come altri hanno suggerito, è improbabile che l'utilizzo di 10 diverse variabili locali con valori booleani sia il modo migliore per scrivere la routine (soprattutto se hanno davvero nomi di una lettera :)

A seconda di ciò che stai facendo, potrebbe avere senso usare un dizionario. Ad esempio, se si desidera impostare valori predefiniti booleani per un set di flag di una lettera, è possibile effettuare ciò:

>>> 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 preferisci, puoi anche farlo con una singola istruzione di assegnazione:

>>> 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}

Il secondo parametro dictnon è interamente progettato per questo: è davvero pensato per permetterti di sovrascrivere singoli elementi del dizionario usando argomenti di parole chiave come d=False. Il codice sopra esplode il risultato dell'espressione seguente **in una serie di argomenti di parole chiave che vengono passati alla funzione chiamata. Questo è certamente un modo affidabile per creare dizionari e le persone sembrano accettare almeno questo idioma, ma sospetto che alcuni potrebbero considerarlo Unpythonic. </disclaimer>


Ancora un altro approccio, che è probabilmente il più intuitivo se si utilizzerà questo modello frequentemente, è quello di definire i dati come un elenco di valori flag ( True, False) mappati ai nomi flag (stringhe a carattere singolo). Quindi si trasforma questa definizione di dati in un dizionario invertito che mappa i nomi dei flag in valori flag. Questo può essere fatto in modo abbastanza succinto con una comprensione dell'elenco nidificato, ma ecco un'implementazione molto leggibile:

>>> 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}

La funzione invert_dictè una funzione del generatore . Si genera , o rese - nel senso che ritorna più volte i valori di - coppie chiave-valore. Quelle coppie chiave-valore sono l'inverso del contenuto dei due elementi del flagsdizionario iniziale . Sono inseriti nel dictcostruttore. In questo caso il dictcostruttore funziona in modo diverso da quanto sopra perché viene alimentato da un iteratore anziché da un dizionario come argomento.


Attingendo al commento di @Chris Lutz: se lo userete davvero per valori a carattere singolo, potete effettivamente farlo

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

Questo funziona perché le stringhe di Python sono ripetibili , il che significa che possono essere spostate in base al valore. Nel caso di una stringa, i valori sono i singoli caratteri nella stringa. Così, quando essi vengono interpretati come iterables, come in questo caso in cui essi vengono utilizzati in un ciclo, ['a', 'b', 'c']e 'abc'sono effettivamente equivalenti. Un altro esempio potrebbe essere quando vengono passati a una funzione che accetta un iterabile, come tuple.

Personalmente non lo farei perché non legge in modo intuitivo: quando vedo una stringa, mi aspetto che venga usato come valore singolo anziché come elenco. Quindi guardo la prima riga e penso "Okay, quindi c'è una bandiera True e una False flag". Quindi, sebbene sia una possibilità, non credo sia la strada da percorrere. Il lato positivo può aiutare a spiegare più chiaramente i concetti di iterabili e iteratori.


Definire la funzione in invert_dictmodo che restituisca effettivamente un dizionario non è neanche una cattiva idea; Per lo più non l'ho fatto solo perché non aiuta davvero a spiegare come funziona la routine.


Apparentemente Python 2.7 ha una comprensione del dizionario, che costituirebbe un modo estremamente conciso per implementare quella funzione. Questo è lasciato come un esercizio al lettore, dal momento che non ho installato Python 2.7 :)

Puoi anche combinare alcune funzioni dal modulo itertools sempre versatile . Come si suol dire, c'è più di un modo per farlo . Aspetta, la gente di Python non lo dice. Bene, è vero comunque in alcuni casi. Immagino che Guido ci abbia fornito le spiegazioni del dizionario in modo che ci sia un modo ovvio per farlo.


1
Si noti che ['a', 'b', 'c']può essere abbreviato list('abc'), il che ispira def truth_values(trues, falses): d = dict.from_keys(list(trues), True); d.update(dict.from_keys(list(falses), False)); return dusato comevalues = truth_values("abc", "de")
Chris Lutz

Grazie mille, questa sembra una risposta molto esauriente, darò una buona occhiata e sperimenterò ciò che hai in prosa, ciò che stai dicendo, mentre è probabilmente vero, potrebbe non essere naturale, quindi dovrei leggere un po 'e giocare fino a quando non avrò pienamente quello che intendi specialmente dal momento che i dizionari sono ancora uno dei miei punti deboli in Python. Grazie mille, tornerò :)
Trufa

5
@intuited Sono sbalordito dalla tua risposta: tu definisci un altro problema rispetto a quello del PO e ti diletti a fare una lunga risposta a questo altro problema. Non vuole associare stringhe e valori in un dizionario, vuole creare oggetti con un identificatore e un valore per ognuno.
eyquem

5
@eyquem: le risposte lunghe sono cattive? Medico, guarisci te stesso!
John Machin,

5
Questo non risponde alla domanda ed è condiscendente. Vedi la risposta di seguito
Kodu,

225
a, b, c, d, e, g, h, i, j = (True,)*9
f = False

21
@Imray La notazione virgola finale (d,)crea una tupla a un elemento, che è un tipo di sequenza. I tipi di sequenza supportano l'aggiunta e la moltiplicazione, tra le altre operazioni.
duozmo,

36
Trucco elegante, ma attenzione all'uso di questo su elementi mutabili come gli elenchi. Per esempio a, b, c = ([],)*3non crea 3 istanze di elenco, ma rende a, be cche punta alla stessa istanza.
Zac,

5
Mi chiedo molto il mio tempo che spreco / spendo cercando di rendere il mio codice Python uber pythonic?
SARose,

3
@Zac per farlo correttamente usare a, b, c = ([] for i in range(3)). fonte . Per coerenza, puoi anche usare una variante di questo per questa risposta, cioè a,b,c,d,e,g,h,i,j = (True for i in range(9)) f=(False i in range(1)).
Novizio C

Questo è un esempio del perché amo Python, usandolo solo di recente .. Vorrei che fosse iniziato prima .. Ma lo sviluppo web varia a seconda del progetto.
Arrabbiato, 84

52

Usa un elenco / dizionario o definisci la tua classe per incapsulare le cose che stai definendo, ma se hai bisogno di tutte quelle variabili puoi fare:

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

1
vars non sarà impostato su True & False.
N 1.1

1
@ N1.1: non riuscivo a capire cosa volevi dire.
Trufa,

@Trufa Se sono impostati solo su Vero / Falso, il modo suggerito è perfetto, ma cosa succede se le variabili sono impostate su cose diverse?
N 1.1

@ N1.1: Ohh ho capito cosa vuoi dire, grazie per il chiarimento! In questo caso, sono tutti bool ma è bene saperlo. Grazie
Trufa

5
Per una spiegazione del perché questo è un modello pericoloso (anche se esempio funzionante), leggi Notorious BIG
duozmo,

10

Questa è un'elaborazione su @ Jeff M e sui miei commenti.

Quando lo fai:

a, b = c, d

Funziona con l'imballaggio e il disimballaggio della tupla. È possibile separare le fasi di imballaggio e disimballaggio:

_ = c, d
a, b = _

La prima riga crea una tupla chiamata _che ha due elementi, la prima con il valore di ce la seconda con il valore di d. La seconda riga decomprime la _tupla nelle variabili ae b. Questo rompe la tua unica grande linea:

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

In due righe più piccole:

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

Ti darà lo stesso esatto risultato della prima riga (inclusa la stessa eccezione se aggiungi valori o variabili a una parte ma dimentichi di aggiornare l'altra). Tuttavia, in questo caso specifico, la risposta di Yan è forse la migliore.

Se hai un elenco di valori, puoi comunque decomprimerli. Devi solo convertirlo in una tupla prima. Ad esempio, il seguente assegnare un valore tra 0 e 9 per ciascuna aattraverso jrispettivamente:

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

EDIT: trucco accurato per assegnarli tutti come veri tranne l'elemento 5 (variabile f):

a, b, c, d, e, f, g, h, i, j = tuple(x != 5 for x in range(10))

Non sapevo che l'ultimo fosse possibile, lo proverò sicuramente, è davvero un trucco accurato e può tornare utile!
Trufa,

5

Quando le persone suggeriscono "usa un elenco o una tupla o un'altra struttura di dati", quello che stanno dicendo è che, quando hai molti valori diversi che ti interessano, nominarli tutti separatamente poiché le variabili locali potrebbero non essere il modo migliore fare cose.

Invece, potresti volerli riunire in una struttura di dati più grande che può essere memorizzata in una singola variabile locale.

intuited ha mostrato come usare un dizionario per questo, e Chris Lutz ha mostrato come usare una tupla per l'archiviazione temporanea prima di decomprimere in variabili separate, ma un'altra opzione da considerare è usare collections.namedtupleper raggruppare i valori in modo permanente.

Quindi potresti fare qualcosa del tipo:

# 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)

Si spera che il codice reale utilizzi nomi più significativi di "DataHolder" e delle lettere dell'alfabeto, ovviamente.


Grazie per la tua risposta, lo verificherò come un'opzione, il fatto è che ( per questo caso particolare ) potrebbe non essere utile avere una struttura immutabile. In seguito commenterò come è andata a finire, ancora molte grazie!
Trufa,

Puoi fare la stessa cosa con una classe normale: la definizione di DataHolderdiventa un po 'più dettagliata.
ncoghlan,

4

Qual è il problema, in effetti?

Se hai davvero bisogno o vuoi 10 a , b , c , d , e , f , g , h , i , j , non ci sarà altra possibilità, in un momento o nell'altro, di scrivere a e scrivere b e scrivere c . ....

Se i valori sono tutti diversi, sarai obbligato a scrivere per esempio

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]

vale a dire definendo le "variabili" individualmente.

Oppure, usando un'altra scrittura, non è necessario utilizzare _:

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]

o

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 alcuni di essi devono avere lo stesso valore, è il problema che è troppo lungo per scrivere

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

?

Quindi puoi scrivere:

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

.

Non capisco qual è esattamente il tuo problema. Se vuoi scrivere un codice, sei obbligato a usare i caratteri richiesti dalla scrittura delle istruzioni e delle definizioni. Cos'altro ?

Mi chiedo se la tua domanda non sia il segno che hai frainteso qualcosa.

Quando si scrive a = 10, non si crea una variabile nel senso di "pezzo di memoria il cui valore può cambiare". Questa istruzione:

  • avvia la creazione di un oggetto di tipo integere valore 10 e l'associazione di un nome "a" con questo oggetto nello spazio dei nomi corrente

  • o riassegnare il nome "a" nello spazio dei nomi all'oggetto 10 (perché "a" era precedentemente associato a un altro oggetto)

Lo dico perché non vedo l'utilità per definire 10 identificatori a, b, c ... che indicano Falso o Vero. Se questi valori non cambiano durante l'esecuzione, perché 10 identificatori? E se cambiano, perché prima definire gli identificatori?, Saranno creati quando necessario se non definiti in precedenza

La tua domanda mi sembra strana


2

Sembra che tu stia affrontando il tuo problema nel modo sbagliato per me.

Riscrivi il codice per utilizzare una tupla o scrivi una classe per archiviare tutti i dati.


Grazie per te puoi dire che senza nemmeno intravedere il codice :) Capisco che non è l'ideale, e potrei dover riformattare ma la tua risposta non risolve davvero la domanda. Capisco però il tuo punto.
Trufa,

1
Posso dire che suona così, sì. È come sembra. Il refactoring probabilmente risolverà il tuo problema. Il tuo problema è un problema di stile, non funzionale, quindi è difficile offrire qualcosa di diverso da piuttosto metadvice.
Richo,

1

Mi piace la risposta più votata; tuttavia, presenta problemi con l'elenco come mostrato.

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

Questo è discusso in dettaglio (qui) , ma l'essenza è quella ae bsono lo stesso oggetto con il a is britorno True(lo stesso per id(a) == id(b)). Pertanto, se si modifica un indice, si sta modificando l'indice di entrambi ae b, poiché sono collegati. Per risolvere questo, puoi fare (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]

Questo può quindi essere usato come una variante della risposta superiore, che ha i risultati intuitivi "desiderati"

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

1

Nel tuo caso, userei YAML.

Questo è uno standard elegante e professionale per la gestione di più parametri. I valori vengono caricati da un file separato. Puoi vedere alcune informazioni in questo link:

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

Ma è più facile per Google, dato che è uno standard, ci sono centinaia di informazioni a riguardo, puoi trovare ciò che si adatta meglio alla tua comprensione. ;)

I migliori saluti.


Ciao Henrique, benvenuto in SO, grazie per la tua risposta! Assicurati di leggere su come scrivere una risposta prima di rispondere alla tua prossima domanda!
Diggy.

0

Come JavaScript , puoi anche usare più istruzioni su una riga in Pythona = 1; b = "Hello World"; c += 3

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.