Elegantere Art, mehrere Variablen gleichzeitig zu deklarieren

140

Um mehrere Variablen gleichzeitig zu deklarieren, würde ich Folgendes tun:

a, b = True, False

Aber wenn ich viel mehr Variablen deklarieren musste, wird es immer weniger elegant:

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

Gibt es eine bessere / elegante / bequeme Möglichkeit, dies zu tun?

Dies muss sehr einfach sein, aber wenn ich eine Liste oder ein Tupel zum Speichern der Variablen verwenden würde, wie müsste ich vorgehen, damit ich hilfreich wäre, da:

aList = [a,b]

Ist nicht gültig, müsste ich tun:

a, b = True, True

Oder was fehlt mir?

Trufa
quelle
Verwenden Sie eine Liste, um diese Werte zu speichern? Ein Wörterbuch? Ein (benanntes) Tupel?
Jeff Mercado
@ Chris: Ich war dort. :)
Jeff Mercado
@ JeffM: Vielleicht, aber ich weiß nicht, wie ich das machen soll. Es scheint, dass sie definiert werden müssen, um zu einer Liste zu gehören (ich kann mich natürlich irren)
Trufa
3
@Trufa: Wenn Sie so viele Variablen zum Speichern von Werten deklarieren möchten, ist dies bereits ein Zeichen, dass Sie meiner Meinung nach andere Speicheralternativen in Betracht ziehen sollten.
Jeff Mercado
1
@ user470379 - Ich habe angenommen, dass die Namen nur für den Beispielcode sind und dass Trufa diese Namen nicht in seinem echten Code verwendet.
Chris Lutz

Antworten:

52

Wie andere vorgeschlagen haben, ist es unwahrscheinlich, dass die Verwendung von 10 verschiedenen lokalen Variablen mit Booleschen Werten der beste Weg ist, um Ihre Routine zu schreiben (insbesondere wenn sie wirklich Ein-Buchstaben-Namen haben :)

Je nachdem, was Sie tun, kann es sinnvoll sein, stattdessen ein Wörterbuch zu verwenden. Wenn Sie beispielsweise boolesche voreingestellte Werte für eine Reihe von Ein-Buchstaben-Flags einrichten möchten, können Sie Folgendes tun:

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

Wenn Sie möchten, können Sie dies auch mit einer einzelnen Zuweisungsanweisung tun:

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

Der zweite Parameter dictist nicht vollständig dafür ausgelegt: Er soll es Ihnen wirklich ermöglichen, einzelne Elemente des Wörterbuchs mit Schlüsselwortargumenten wie zu überschreiben d=False. Der obige Code sprengt das Ergebnis des folgenden Ausdrucks **in eine Reihe von Schlüsselwortargumenten, die an die aufgerufene Funktion übergeben werden. Dies ist sicherlich ein zuverlässiger Weg, um Wörterbücher zu erstellen, und die Leute scheinen diese Redewendung zumindest zu akzeptieren, aber ich vermute, dass einige sie für unpythonisch halten. </disclaimer>


Ein weiterer Ansatz, der wahrscheinlich am intuitivsten ist, wenn Sie dieses Muster häufig verwenden, besteht darin, Ihre Daten als eine Liste von Flag-Werten ( True, False) zu definieren, die Flag-Namen (Einzelzeichenfolgen) zugeordnet sind. Anschließend transformieren Sie diese Datendefinition in ein invertiertes Wörterbuch, das Flaggennamen Flaggenwerten zuordnet. Dies kann mit einem verschachtelten Listenverständnis recht prägnant durchgeführt werden, aber hier ist eine sehr lesbare Implementierung:

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

Die Funktion invert_dictist eine Generatorfunktion . Es generiert oder liefert - was bedeutet, dass es wiederholt Werte von - Schlüssel-Wert-Paaren zurückgibt . Diese Schlüssel-Wert-Paare sind die Umkehrung des Inhalts der beiden Elemente des ursprünglichen flagsWörterbuchs. Sie werden in den dictKonstruktor eingespeist . In diesem Fall dictfunktioniert der Konstruktor anders als oben, da ihm als Argument eher ein Iterator als ein Wörterbuch zugeführt wird .


Gestützt auf den Kommentar von @Chris Lutz: Wenn Sie dies wirklich für Einzelzeichenwerte verwenden, können Sie dies tatsächlich tun

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

Dies funktioniert, weil Python-Zeichenfolgen iterierbar sind , was bedeutet, dass sie Wert für Wert verschoben werden können. Bei einer Zeichenfolge sind die Werte die einzelnen Zeichen in der Zeichenfolge. Wenn sie also als iterable interpretiert werden, wie in diesem Fall, wenn sie in einer for-Schleife verwendet werden, ['a', 'b', 'c']und 'abc'effektiv äquivalent sind. Ein anderes Beispiel wäre, wenn sie an eine Funktion übergeben werden, die eine iterierbare Funktion wie z tuple.

Ich persönlich würde dies nicht tun, weil es nicht intuitiv zu lesen ist: Wenn ich eine Zeichenfolge sehe, erwarte ich, dass sie als einzelner Wert und nicht als Liste verwendet wird. Also schaue ich in die erste Zeile und denke: "Okay, es gibt also eine wahre und eine falsche Flagge." Obwohl dies eine Möglichkeit ist, denke ich nicht, dass dies der richtige Weg ist. Auf der anderen Seite kann es hilfreich sein, die Konzepte von Iterablen und Iteratoren klarer zu erklären.


invert_dictEs ist auch keine schlechte Idee, die Funktion so zu definieren, dass sie tatsächlich ein Wörterbuch zurückgibt. Ich habe das meistens einfach nicht gemacht, weil es nicht wirklich hilft zu erklären, wie die Routine funktioniert.


Anscheinend verfügt Python 2.7 über Wörterbuchverständnisse, die eine äußerst präzise Implementierung dieser Funktion ermöglichen würden. Dies bleibt dem Leser als Übung überlassen, da ich Python 2.7 nicht installiert habe :)

Sie können auch einige Funktionen des vielseitigen itertools- Moduls kombinieren . Wie sie sagen, gibt es mehr als einen Weg, es zu tun . Warten Sie, die Python-Leute sagen das nicht. Nun, in einigen Fällen ist es sowieso wahr. Ich würde vermuten, dass Guido uns Wörterbuchverständnisse gegeben hat, damit es einen offensichtlichen Weg gibt , dies zu tun.

intuitiv
quelle
1
Beachten Sie, ['a', 'b', 'c']dass verkürzt werden kann list('abc'), was inspiriert def truth_values(trues, falses): d = dict.from_keys(list(trues), True); d.update(dict.from_keys(list(falses), False)); return dalsvalues = truth_values("abc", "de")
Chris Lutz
Vielen Dank, dies scheint eine sehr umfassende Antwort zu sein. Ich werde es gut durchsehen und mit dem experimentieren, was Sie prosaieren, was Sie sagen, obwohl es wahrscheinlich wahr ist, ist es im Mai nicht selbstverständlich, also muss ich es tun Lies ein bisschen und spiele herum, bis ich vollständig verstanden habe, was du meinst, besonders seit Wörterbüchern, die eine meiner Schwächen in Python sind. Vielen Dank, ich komme wieder :)
Trufa
5
@intuited Ich bin verblüfft über Ihre Antwort: Sie definieren ein anderes Problem als das des OP und freuen sich, eine lange Antwort auf dieses andere Problem zu geben. Er möchte keine Zeichenfolgen und Werte in einem Wörterbuch verknüpfen, sondern Objekte mit einem Bezeichner und einem Wert für jeden erstellen.
Eyquem
5
@eyquem: Lange Antworten sind schlecht? Arzt, heile dich selbst!
John Machin
5
Dies beantwortet die Frage nicht und ist bevormundend. Siehe die Antwort unten
Kodu
225
a, b, c, d, e, g, h, i, j = (True,)*9
f = False
Shariq
quelle
21
@Imray Nach der Komma-Notation (d,)wird ein Tupel mit einem Element erstellt, bei dem es sich um einen Sequenztyp handelt. Sequenztypen unterstützen unter anderem die Addition und Multiplikation.
Duozmo
36
Eleganter Trick, aber beachten Sie, dass Sie diesen für veränderbare Elemente wie Listen verwenden können. Zum Beispiel a, b, c = ([],)*3schafft keine 3 Listeninstanzen , sondern macht a, bund czeigt auf die gleiche Instanz.
Zac
5
Ich frage mich, wie viel Zeit ich damit verbringe, meinen Python-Code überpythonisch zu machen.
SARose
3
@Zac dazu richtig verwenden a, b, c = ([] for i in range(3)). Quelle . Aus Gründen der Konsistenz können Sie für diese Antwort auch eine Variante davon verwenden, z a,b,c,d,e,g,h,i,j = (True for i in range(9)) f=(False i in range(1)).
Anfänger C
Dies ist ein Beispiel dafür, warum ich Python liebe. Ich benutze es erst kürzlich. Ich wünschte, es würde früher begonnen. Aber die Webentwicklung variiert je nach Projekt.
Wütend 84
52

Verwenden Sie eine Liste / ein Wörterbuch oder definieren Sie Ihre eigene Klasse, um die von Ihnen definierten Elemente zu kapseln. Wenn Sie jedoch alle diese Variablen benötigen, können Sie Folgendes tun:

a = b = c = d = e = g = h = i = j = True
f = False
Yan
quelle
1
vars wird nicht nur auf True & False gesetzt.
N 1.1
1
@ N1.1: Ich konnte nicht verstehen, was du damit gemeint hast.
Trufa
@Trufa Wenn sie nur auf Wahr / Falsch gesetzt sind, ist der vorgeschlagene Weg perfekt, aber was ist, wenn Variablen auf verschiedene Dinge gesetzt sind?
N 1.1
@ N1.1: Ohh ich verstehe was du meinst, danke für die Klarstellung! In diesem Fall sind sie alle Narren, aber es ist gut zu wissen. Danke
Trufa
5
Eine Erklärung, warum dies ein gefährliches Muster ist (wenn auch ein funktionierendes Beispiel), finden Sie in Notorious BIG
duozmo
10

Dies ist eine Ausarbeitung von @ Jeff Ms und meinen Kommentaren.

Wenn Sie dies tun:

a, b = c, d

Es funktioniert mit Tupelpacken und -auspacken. Sie können die Schritte zum Ein- und Auspacken trennen:

_ = c, d
a, b = _

In der ersten Zeile wird ein aufgerufenes Tupel erstellt, _das zwei Elemente enthält, das erste mit dem Wert von cund das zweite mit dem Wert von d. Die zweite Zeile entpackt das _Tupel in die Variablen aund b. Dies bricht Ihre eine große Linie zusammen:

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

In zwei kleinere Zeilen:

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

Sie erhalten genau das gleiche Ergebnis wie in der ersten Zeile (einschließlich derselben Ausnahme, wenn Sie einem Teil Werte oder Variablen hinzufügen, den anderen jedoch vergessen, zu aktualisieren). In diesem speziellen Fall ist Yans Antwort jedoch vielleicht die beste.

Wenn Sie eine Liste mit Werten haben, können Sie diese trotzdem entpacken. Sie müssen es nur zuerst in ein Tupel konvertieren. Im Folgenden wird beispielsweise jedem aDurchgang ein Wert zwischen 0 und 9 zugewiesen j:

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

EDIT: Ordentlicher Trick, um alle als wahr zuzuweisen, außer Element 5 (Variable f):

a, b, c, d, e, f, g, h, i, j = tuple(x != 5 for x in range(10))
Chris Lutz
quelle
Ich wusste nicht, dass der letzte möglich ist, ich werde es auf jeden Fall versuchen, es ist in der Tat ein ordentlicher Trick und es kann nützlich sein!
Trufa
5

Wenn Leute vorschlagen, "eine Liste, ein Tupel oder eine andere Datenstruktur zu verwenden", sagen sie, dass es möglicherweise nicht der beste Weg ist, sie alle separat als lokale Variablen zu benennen, wenn Sie viele verschiedene Werte haben, die Ihnen wichtig sind Sachen machen.

Stattdessen möchten Sie sie möglicherweise zu einer größeren Datenstruktur zusammenfassen, die in einer einzelnen lokalen Variablen gespeichert werden kann.

intuited zeigte, wie Sie ein Wörterbuch dafür verwenden können, und Chris Lutz zeigte, wie Sie ein Tupel für die temporäre Speicherung verwenden, bevor Sie es in separate Variablen entpacken. Eine weitere Option, die Sie in Betracht ziehen sollten, besteht darin collections.namedtuple, die Werte dauerhafter zu bündeln.

Sie könnten also etwas tun wie:

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

Echter Code würde hoffentlich aussagekräftigere Namen verwenden als "DataHolder" und natürlich die Buchstaben des Alphabets.

ncoghlan
quelle
Vielen Dank für Ihre Antwort. Ich werde dies als Option prüfen. Die Sache ist, dass es ( für diesen speziellen Fall ) möglicherweise nicht sinnvoll ist, eine unveränderliche Struktur zu haben. Ich werde später kommentieren, wie sich das herausstellte, nochmals vielen Dank!
Trufa
Sie können dasselbe mit einer gewöhnlichen Klasse tun - die Definition von wird DataHoldernur etwas ausführlicher.
Ncoghlan
4

Was ist eigentlich das Problem?

Wenn Sie wirklich 10 brauchen oder wollen , ein , b , c , d , e , f , g , h , i , j , wird es keine andere Möglichkeit geben, zu einer Zeit oder einem anderen, schreiben ein und schreiben b und Schreib c . ....

Wenn die Werte alle unterschiedlich sind, müssen Sie beispielsweise schreiben

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]

das heißt, die "Variablen" einzeln definieren.

Oder wenn Sie eine andere Schrift verwenden, müssen Sie nicht Folgendes verwenden _:

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]

oder

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

.

Wenn einige von ihnen den gleichen Wert haben müssen, ist das Problem, dass das Schreiben zu lang ist

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

?

Dann können Sie schreiben:

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

.

Ich verstehe nicht, was genau dein Problem ist. Wenn Sie einen Code schreiben möchten, müssen Sie die Zeichen verwenden, die für das Schreiben der Anweisungen und Definitionen erforderlich sind. Was sonst ?

Ich frage mich, ob Ihre Frage nicht das Zeichen dafür ist, dass Sie etwas falsch verstehen.

Wenn man schreibt a = 10, erstellt man keine Variable im Sinne von "Speicherblock, dessen Wert sich ändern kann". Diese Anweisung:

  • Entweder wird die Erstellung eines Objekts vom Typ integerund Wert 10 und die Bindung eines Namens 'a' mit diesem Objekt im aktuellen Namespace ausgelöst

  • oder weisen Sie dem Objekt 10 den Namen 'a' im Namespace neu zu (weil 'a' zuvor an ein anderes Objekt gebunden wurde)

Ich sage das, weil ich das Dienstprogramm zum Definieren von 10 Bezeichnern a, b, c ... nicht sehe, die auf False oder True zeigen. Wenn sich diese Werte während der Ausführung nicht ändern, warum 10 Bezeichner? Und wenn sie sich ändern, warum zuerst die Bezeichner definieren? Werden sie bei Bedarf erstellt, wenn sie nicht zuvor definiert wurden

Ihre Frage erscheint mir komisch

eyquem
quelle
2

Klingt so, als würden Sie sich Ihrem Problem falsch nähern.

Schreiben Sie Ihren Code neu, um ein Tupel zu verwenden, oder schreiben Sie eine Klasse, um alle Daten zu speichern.

Richo
quelle
Vielen Dank, dass Sie das sagen können, ohne auch nur einen Blick auf den Code zu werfen :) Ich verstehe, dass dies nicht ideal ist, und ich muss es möglicherweise neu formatieren, aber Ihre Antwort löst die Frage nicht wirklich. Ich verstehe Ihren Standpunkt jedoch.
Trufa
1
Ich kann sagen, dass es so klingt, ja. So klingt es. Refactoring wird wahrscheinlich Ihr Problem lösen. Ihr Problem ist ein Stilproblem, kein funktionales, daher ist es schwierig, etwas anderes als Metadvice anzubieten.
Richo
1

Ich mag die Antwort mit der höchsten Abstimmung. Es gibt jedoch Probleme mit der Liste wie gezeigt.

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

Dies wird in großen Details besprochen (hier) , aber das Wesentliche ist , dass aund bdas gleiche Objekt mit der a is bRückkehr True(gleich für id(a) == id(b)). Wenn Sie also einen Index ändern, ändern Sie den Index von beiden aund b, da diese verknüpft sind. Um dies zu lösen, können Sie (Quelle)

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

Dies kann dann als Variante der Top-Antwort verwendet werden, die die "gewünschten" intuitiven Ergebnisse liefert

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

In Ihrem Fall würde ich YAML verwenden.

Das ist ein eleganter und professioneller Standard für den Umgang mit mehreren Parametern. Die Werte werden aus einer separaten Datei geladen. Sie können einige Informationen in diesem Link sehen:

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

Aber es ist einfacher, es zu googeln, da es ein Standard ist, es gibt Hunderte von Informationen darüber, Sie können herausfinden, was am besten zu Ihrem Verständnis passt. ;)

Freundliche Grüße.

Henrique LR
quelle
Hallo Henrique, willkommen bei SO, danke für deine Antwort! Bitte lesen Sie unbedingt eine Antwort, bevor Sie Ihre nächste Frage beantworten!
Diggy.
0

Wie bei JavaScript können Sie auch in Python mehrere Anweisungen in einer Zeile verwendena = 1; b = "Hello World"; c += 3

Isaac Frost
quelle