Wie teste ich mehrere Variablen gegen einen Wert?

643

Ich versuche, eine Funktion zu erstellen, die mehrere Variablen mit einer Ganzzahl vergleicht und eine Zeichenfolge aus drei Buchstaben ausgibt. Ich habe mich gefragt, ob es eine Möglichkeit gibt, dies in Python zu übersetzen. Sozusagen:

x = 0
y = 1
z = 3
mylist = []

if x or y or z == 0 :
    mylist.append("c")
if x or y or z == 1 :
    mylist.append("d")
if x or y or z == 2 :
    mylist.append("e")
if x or y or z == 3 : 
    mylist.append("f")

welches eine Liste von zurückgeben würde:

["c", "d", "f"]

Ist so etwas möglich?

user1877442
quelle
5
Verwendung 1in (Tupel)
2
Wenn Sie eine Liste von Anweisungen auf beliebige Weise auswerten möchten, können Sie any/ allfunctions verwenden. Zum Beispiel: all([1, 2, 3, 4, False])wird zurückgegeben False all([True, 1, 2, 3])wird zurückgegeben True any([False, 0, 0, False])wird zurückgegeben False any([False, 0, True, False])wird True zurückgeben
eddd
4
Diese Frage ist ein sehr beliebtes doppeltes Ziel, aber ich denke, sie ist für diesen Zweck nicht optimal. Die meisten Leute versuchen so etwas zu tun if x == 0 or 1:, was natürlich ähnlich ist if x or y == 0:, aber für Neulinge dennoch etwas verwirrend sein könnte. Angesichts der schieren Lautstärke von "Warum x == 0 or 1arbeite ich nicht ?" Fragen, ich würde diese Frage viel lieber als unser kanonisches doppeltes Ziel für diese Fragen verwenden.
Aran-Fey
1
Seien Sie besonders vorsichtig , wenn Sie auf „Falsey“ Werte wie zu vergleichen 0, 0.0oder False. Sie können leicht falschen Code schreiben, der die "richtige" Antwort gibt.
smci
1
Das Gegenteil finden Sie unter Vergleichen einer Zeichenfolge mit mehreren Elementen in Python
Tripleee

Antworten:

848

Sie verstehen falsch, wie boolesche Ausdrücke funktionieren. Sie funktionieren nicht wie ein englischer Satz und vermuten, dass Sie hier für alle Namen über denselben Vergleich sprechen. Du suchst nach:

if x == 1 or y == 1 or z == 1:

xund yansonsten auf ihre eigenen ausgewertet ( Falsewenn 0, Trueanders).

Sie können dies mit einem Eindämmungstest gegen ein Tupel verkürzen :

if 1 in (x, y, z):

oder noch besser:

if 1 in {x, y, z}:

Verwenden von aset , um den Mitgliedschaftstest mit konstanten Kosten zu nutzen ( indauert unabhängig vom linken Operanden eine feste Zeit).

Wenn Sie verwenden or, sieht Python jede Seite des Operators als separate Ausdrücke. Der Ausdruck x or y == 1wird zuerst als boolescher Test behandelt. xWenn dies falsch ist, wird der Ausdruck y == 1getestet.

Dies liegt an der Priorität des Bedieners . Der orBediener hat eine niedrigere Priorität als der ==Test, daher wird letzterer zuerst bewertet .

Selbst wenn dies nicht der Fall wäre und der Ausdruck x or y or z == 1tatsächlich so interpretiert (x or y or z) == 1würde, würde dies immer noch nicht das tun, was Sie von ihm erwarten.

x or y or zwürde bis zum ersten Argument auswerten, das 'wahr' ist, z. B. nicht False, numerisch 0 oder leer (siehe boolesche Ausdrücke für Details darüber, was Python in einem booleschen Kontext als falsch betrachtet).

Also für die Werte x = 2; y = 1; z = 0, x or y or zwürde lösen zu 2, denn das ist der erste echte ähnliche Wert in den Argumenten ist. Dann 2 == 1wäre es False, obwohl es so y == 1wäre True.

Gleiches würde für die Umkehrung gelten; Testen mehrerer Werte gegen eine einzelne Variable; x == 1 or 2 or 3würde aus den gleichen Gründen scheitern. Verwenden Sie x == 1 or x == 2 or x == 3oder x in {1, 2, 3}.

Martijn Pieters
quelle
115
Ich würde mich nicht so schnell für die setVersion entscheiden. Tupel sind sehr billig zu erstellen und zu wiederholen. Zumindest auf meinem Computer sind Tupel schneller als Sätze, solange die Größe des Tupels etwa 4-8 Elemente beträgt. Wenn Sie mehr scannen müssen, verwenden Sie ein Set. Wenn Sie jedoch nach einem Objekt mit 2 bis 4 Möglichkeiten suchen, ist ein Tupel immer noch schneller! Wenn Sie für die wahrscheinlichste Fall anordnen können in dem Tupel ersten sein, ist der Gewinn noch größer: (mein Test: timeit.timeit('0 in {seq}'.format(seq=tuple(range(9, -1, -1)))))
SingleNegationElimination
56
@dequestarmappartialsetattr: In Python 3.3 und höher wird die Menge als Konstante gespeichert, wobei die Erstellungszeit insgesamt umgangen wird und die Erstellungszeit entfällt. Das Erstellen von Tupeln kann billig sein, da Python ein Bündel davon zwischenspeichert, um Speicherabwanderung zu vermeiden. Dies ist der größte Unterschied zu Sets hier.
Martijn Pieters
13
@dequestarmappartialsetattr: Wenn Sie nur den Mitgliedschaftstest zeitlich festlegen , sind für Ganzzahlen Mengen und Tupel für das ideale Szenario gleich schnell. passend zum ersten Element. Danach verlieren Tupel an Sets.
Martijn Pieters
17
@MartijnPieters: Die Verwendung der Literalnotation setfür diesen Test ist keine Ersparnis, es sei denn, der Inhalt des setLiteral ist auch ein Literal, oder? if 1 in {x, y, z}:kann das nicht cachen set, weil x, yund zkönnte sich ändern, so braucht entweder Lösung ein bauen tupleoder setvon Grund auf neu, und ich vermute , was Einsparungen Nachschlag Sie erhalten könnten , wenn für die Mitgliedschaft Überprüfung durch mehr überschwemmt werden würde setErstellungszeit.
ShadowRanger
9
@ShadowRanger: Ja, die Gucklochoptimierung (sei es für in [...]oder in {...}) funktioniert nur, wenn der Inhalt der Liste oder des Satzes auch unveränderliche Literale sind.
Martijn Pieters
96

Ihr Problem lässt sich leichter mit einer Wörterbuchstruktur wie der folgenden beheben:

x = 0
y = 1
z = 3
d = {0: 'c', 1:'d', 2:'e', 3:'f'}
mylist = [d[k] for k in [x, y, z]]
Dansalmo
quelle
21
Oder sogar d = "cdef"was zuMyList = ["cdef"[k] for k in [x, y, z]]
Aragaer
9
odermap(lambda i: 'cdef'[i], [x, y, z])
Dansalmo
3
@MJM Die Ausgabereihenfolge wird nicht durch das Diktat bestimmt, sondern durch die Reihenfolge der Liste[x, y, z]
Dansalmo
1
Abgesehen von dem Listenverständnis, an das ich noch nicht ganz gewöhnt bin, hatten die meisten von uns den gleichen Reflex: Bauen Sie dieses Diktat auf!
LoneWanderer
66

Wie von Martijn Pieters angegeben, ist das richtige und schnellste Format:

if 1 in {x, y, z}:

Mit seinem Rat hätten Sie jetzt separate if-Anweisungen, damit Python jede Anweisung lesen kann, unabhängig davon, ob die ersteren waren Trueoder nicht False. Sowie:

if 0 in {x, y, z}:
    mylist.append("c")
if 1 in {x, y, z}:
    mylist.append("d")
if 2 in {x, y, z}:
    mylist.append("e")
...

Dies wird funktionieren, aber wenn Sie mit der Verwendung von Wörterbüchern vertraut sind (siehe, was ich dort getan habe), können Sie dies bereinigen, indem Sie ein erstes Wörterbuch erstellen, in dem die Zahlen den gewünschten Buchstaben zugeordnet sind, und dann einfach eine for-Schleife verwenden:

num_to_letters = {0: "c", 1: "d", 2: "e", 3: "f"}
for number in num_to_letters:
    if number in {x, y, z}:
        mylist.append(num_to_letters[number])
ThatGuyRussell
quelle
46

Der direkte Weg zu schreiben x or y or z == 0ist

if any(map((lambda value: value == 0), (x,y,z))):
    pass # write your logic.

Aber ich glaube nicht, dass es dir gefällt. :) Und dieser Weg ist hässlich.

Der andere Weg (ein besserer) ist:

0 in (x, y, z)

Übrigens ifkönnten viele s so geschrieben werden

my_cases = {
    0: Mylist.append("c"),
    1: Mylist.append("d")
    # ..
}

for key in my_cases:
    if key in (x,y,z):
        my_cases[key]()
        break
akaRem
quelle
8
In Ihrem Beispiel für dictanstelle eines Schlüssels erhalten Sie Fehler, da der Rückgabewert von .appendis Noneist und der Aufruf ein Noneergibt AttributeError. Im Allgemeinen stimme ich dieser Methode jedoch zu.
SethMMorton
2
Das Diktat anstelle eines Schlüssels ist falsch. Sie erhalten Mylist = ['c', 'd'], wenn das Wörterbuch initialisiert wird, auch wenn Sie den Teil "for..loop" auskommentiert haben
Mahmoud Elshahat
1
In Ihrem ersten Beispiel filterwäre besser als map, da es nur Fälle zurückgibt, in denen Lambda als wahr ausgewertet wird
Alex
1
Ein Verständnis ist viel einfacher als eine Karte eines Lambda:any(v == 0 for v in (x, y, z))
Wjandrea
35

Wenn Sie sehr, sehr faul sind, können Sie die Werte in ein Array einfügen. Sowie

list = []
list.append(x)
list.append(y)
list.append(z)
nums = [add numbers here]
letters = [add corresponding letters here]
for index in range(len(nums)):
    for obj in list:
        if obj == num[index]:
            MyList.append(letters[index])
            break

Sie können die Zahlen und Buchstaben auch in ein Wörterbuch einfügen und dies tun, aber dies ist wahrscheinlich VIEL komplizierter als nur if-Anweisungen. Das bekommst du, wenn du versuchst, besonders faul zu sein :)

Noch eine Sache, deine

if x or y or z == 0:

wird kompiliert, aber nicht so, wie Sie es möchten. Wenn Sie einfach eine Variable in eine if-Anweisung einfügen (Beispiel)

if b

Das Programm prüft, ob die Variable nicht null ist. Eine andere Möglichkeit, die obige Aussage zu schreiben (was sinnvoller ist), ist

if bool(b)

Bool ist eine in Python eingebaute Funktion, die im Grunde den Befehl zum Überprüfen einer booleschen Anweisung ausführt (Wenn Sie nicht wissen, was das ist, versuchen Sie es gerade in Ihrer if-Anweisung :))

Ein anderer fauler Weg, den ich gefunden habe, ist:

if any([x==0, y==0, z==0])
ytpillai
quelle
3
-1 Hier wird viel schlecht geübt. listist ein Python eingebaut; Verwenden Sie stattdessen einen anderen Namen, wie xyzzum Beispiel. Warum erstellen Sie die Liste in vier Schritten, wenn Sie einen ausführen können, dh xyz = [x, y, z]? Verwenden Sie keine parallelen Listen, sondern ein Diktat. Alles in allem ist diese Lösung viel komplizierter als die von ThatGuyRussell . Warum nicht auch für den letzten Teil ein Verständnis machen, dh any(v == 0 for v in (x, y, z))? Auch Arrays sind etwas anderes in Python.
Wjandrea
32

Um zu überprüfen, ob ein Wert in einer Reihe von Variablen enthalten ist, können Sie die eingebauten Module itertoolsund verwenden operator.

Zum Beispiel:

Importe:

from itertools import repeat
from operator import contains

Variablen deklarieren:

x = 0
y = 1
z = 3

Erstellen Sie eine Zuordnung von Werten (in der Reihenfolge, die Sie überprüfen möchten):

check_values = (0, 1, 3)

Verwenden Sie itertoolsdiese Option , um die Wiederholung der Variablen zu ermöglichen:

check_vars = repeat((x, y, z))

Verwenden Sie mapabschließend die Funktion, um einen Iterator zu erstellen:

checker = map(contains, check_vars, check_values)

Verwenden Sie dann bei der Überprüfung der Werte (in der ursprünglichen Reihenfolge) next():

if next(checker)  # Checks for 0
    # Do something
    pass
elif next(checker)  # Checks for 1
    # Do something
    pass

usw...

Dies hat einen Vorteil gegenüber der , lambda x: x in (variables)weil operatorein eingebautes Modul und ist schneller und effizienter als die Verwendung , lambdadie eine individuelle Vor-Ort - Funktion zu erstellen hat.

Eine weitere Option zum Überprüfen, ob eine Liste einen Wert ungleich Null (oder Falsch) enthält:

not (x and y and z)

Äquivalent:

not all((x, y, z))
Schuldiger Delphin
quelle
2
Dies beantwortet die Frage des OP nicht. Es wird nur der erste Fall im angegebenen Beispiel behandelt.
Wallacer
31

Set ist hier der gute Ansatz, weil es die Variablen ordnet, was hier Ihr Ziel zu sein scheint. {z,y,x}ist {0,1,3}unabhängig von der Reihenfolge der Parameter.

>>> ["cdef"[i] for i in {z,x,y}]
['c', 'd', 'f']

Auf diese Weise ist die gesamte Lösung O (n).

BM
quelle
5
Sie sollten eine Beschreibung hinzufügen, was Ihr Code leistet und wie er es tut. Von kurzen Antworten, die nur Code verwenden, wird abgeraten
Raniz
31

Alle hier if 1 in {x,y,z}gegebenen hervorragenden Antworten konzentrieren sich auf die spezifischen Anforderungen des Originalplakats und auf die von Martijn Pieters vorgeschlagene Lösung.
Was sie ignorieren, ist die umfassendere Implikation der Frage:
Wie teste ich eine Variable gegen mehrere Werte?
Die bereitgestellte Lösung funktioniert nicht für Teiltreffer, wenn beispielsweise Zeichenfolgen verwendet werden:
Testen Sie, ob die Zeichenfolge "Wild" mehrere Werte enthält

>>> x = "Wild things"
>>> y = "throttle it back"
>>> z = "in the beginning"
>>> if "Wild" in {x, y, z}: print (True)
... 

oder

>>> x = "Wild things"
>>> y = "throttle it back"
>>> z = "in the beginning"
>>> if "Wild" in [x, y, z]: print (True)
... 

In diesem Szenario ist es am einfachsten, in eine Zeichenfolge zu konvertieren

>>> [x, y, z]
['Wild things', 'throttle it back', 'in the beginning']
>>> {x, y, z}
{'in the beginning', 'throttle it back', 'Wild things'}
>>> 

>>> if "Wild" in str([x, y, z]): print (True)
... 
True
>>> if "Wild" in str({x, y, z}): print (True)
... 
True

Es ist jedoch zu beachten @codeforester, dass , wie erwähnt , bei dieser Methode Wortgrenzen verloren gehen, wie in:

>>> x=['Wild things', 'throttle it back', 'in the beginning']
>>> if "rot" in str(x): print(True)
... 
True

Die 3 Buchstaben rotsind in der Liste in Kombination vorhanden, jedoch nicht als einzelnes Wort. Das Testen auf "rot" würde fehlschlagen, aber wenn eines der Listenelemente "rot in hell" wäre, würde dies ebenfalls fehlschlagen.
Das Ergebnis ist, seien Sie vorsichtig mit Ihren Suchkriterien, wenn Sie diese Methode verwenden, und beachten Sie, dass diese Einschränkung vorliegt.

Rolf von Sachsen
quelle
30

Ich denke, das wird besser damit umgehen:

my_dict = {0: "c", 1: "d", 2: "e", 3: "f"}

def validate(x, y, z):
    for ele in [x, y, z]:
        if ele in my_dict.keys():
            return my_dict[ele]

Ausgabe:

print validate(0, 8, 9)
c
print validate(9, 8, 9)
None
print validate(9, 8, 2)
e
Bhargav Boda
quelle
30

Wenn Sie if verwenden möchten, sind die folgenden Anweisungen eine andere Lösung:

myList = []
aList = [0, 1, 3]

for l in aList:
    if l==0: myList.append('c')
    elif l==1: myList.append('d')
    elif l==2: myList.append('e')
    elif l==3: myList.append('f')

print(myList)
hamid
quelle
26
d = {0:'c', 1:'d', 2:'e', 3: 'f'}
x, y, z = (0, 1, 3)
print [v for (k,v) in d.items() if x==k or y==k or z==k]
Saksham Varma
quelle
26

Dieser Code kann hilfreich sein

L ={x, y, z}
T= ((0,"c"),(1,"d"),(2,"e"),(3,"f"),)
List2=[]
for t in T :
if t[0] in L :
    List2.append(t[1])
    break;
michael zxc858
quelle
12

Sie können die unten gezeigte Methode ausprobieren. Bei dieser Methode haben Sie die Freiheit, die Anzahl der Variablen anzugeben / einzugeben, die Sie eingeben möchten.

mydict = {0:"c", 1:"d", 2:"e", 3:"f"}
mylist= []

num_var = int(raw_input("How many variables? ")) #Enter 3 when asked for input.

for i in range(num_var): 
    ''' Enter 0 as first input, 1 as second input and 3 as third input.'''
    globals()['var'+str('i').zfill(3)] = int(raw_input("Enter an integer between 0 and 3 "))
    mylist += mydict[globals()['var'+str('i').zfill(3)]]

print mylist
>>> ['c', 'd', 'f']
Siddharth Satpathy
quelle
10

Einzeilige Lösung:

mylist = [{0: 'c', 1: 'd', 2: 'e', 3: 'f'}[i] for i in [0, 1, 2, 3] if i in (x, y, z)]

Oder:

mylist = ['cdef'[i] for i in range(4) if i in (x, y, z)]
Vinayak Kaniyarakkal
quelle
9

Möglicherweise benötigen Sie eine direkte Formel für die gesetzten Ausgangsbits.

x=0 or y=0 or z=0   is equivalent to x*y*z = 0

x=1 or y=1 or z=1   is equivalent to (x-1)*(y-1)*(z-1)=0

x=2 or y=2 or z=2   is equivalent to (x-2)*(y-2)*(z-2)=0

Lassen Sie uns auf Bits abbilden: 'c':1 'd':0xb10 'e':0xb100 'f':0xb1000

Beziehung von isc (ist 'c'):

if xyz=0 then isc=1 else isc=0

Verwenden Sie die mathematische Formel https://youtu.be/KAdKCgBGK0k?list=PLnI9xbPdZUAmUL8htSl6vToPQRRN3hhFp&t=315

[c]: (xyz=0 and isc=1) or (((xyz=0 and isc=1) or (isc=0)) and (isc=0))

[d]: ((x-1)(y-1)(z-1)=0 and isc=2) or (((xyz=0 and isd=2) or (isc=0)) and (isc=0))

...

Verbinden Sie diese Formeln mit folgender Logik:

  • Logik andist die Summe der Gleichungsquadrate
  • Logik orist das Produkt von Gleichungen

und Sie haben eine Gesamtgleichung Express Summe und Sie haben eine Gesamtformel der Summe

dann ist Summe & 1 c, Summe & 2 ist d, Summe & 4 ist e, Summe & 5 ist f

Danach können Sie ein vordefiniertes Array bilden, in dem der Index der Zeichenfolgenelemente der fertigen Zeichenfolge entspricht.

array[sum] gibt Ihnen die Zeichenfolge.

Sergei
quelle
7

Es kann leicht gemacht werden als

for value in [var1,var2,var3]:
     li.append("targetValue")
Seenivasan
quelle
6

Die mnemonischste Art, Ihren Pseudocode in Python darzustellen, wäre:

x = 0
y = 1
z = 3
mylist = []

if any(v == 0 for v in (x, y, z)):
    mylist.append("c")
if any(v == 1 for v in (x, y, z)):
    mylist.append("d")
if any(v == 2 for v in (x, y, z)):
    mylist.append("e")
if any(v == 3 for v in (x, y, z)):
    mylist.append("f")
rsalmei
quelle
1
Dieser Ansatz ist universeller als "wenn 2 in (x, y, z): mylist.append (" e ")", da willkürliche Vergleiche möglich sind (z if any(v >= 42 for v in (x, y, z)):. B. ). Und Leistung aller drei Methoden ( 2 in {x,y,z}, 2 in (x,y,z), any(_v == 2 for _v in (x,y,z))) in CPython3.6 fast das gleiche zu sein scheint (siehe Gist )
imposeren
5

So testen Sie mehrere Variablen mit einem einzigen Wert: if 1 in {a,b,c}:

So testen Sie mehrere Werte mit einer Variablen: if a in {1, 2, 3}:

Alamin
quelle
4

Sieht so aus, als würden Sie eine Art Caesar-Chiffre bauen.

Ein viel allgemeinerer Ansatz ist folgender:

input_values = (0, 1, 3)
origo = ord('c')
[chr(val + origo) for val in inputs]

Ausgänge

['c', 'd', 'f']

Sie sind sich nicht sicher, ob es sich um einen gewünschten Nebeneffekt Ihres Codes handelt, aber die Reihenfolge Ihrer Ausgabe wird immer sortiert.

Wenn Sie dies wünschen, kann die letzte Zeile geändert werden in:

sorted([chr(val + origo) for val in inputs])
Firelynx
quelle
2

Sie können das Wörterbuch verwenden:

x = 0
y = 1
z = 3
list=[]
dict = {0: 'c', 1: 'd', 2: 'e', 3: 'f'}
if x in dict:
    list.append(dict[x])
else:
    pass

if y in dict:
    list.append(dict[y])
else:
    pass
if z in dict:
    list.append(dict[z])
else:
    pass

print list
Rohit Gawas
quelle
1
Dies kann mehr als einmal angehängt werden. Einstellen?
Sergei
2

Versuchen Sie diese Lösung ohne Diktat:

x, y, z = 0, 1, 3    
offset = ord('c')
[chr(i + offset) for i in (x,y,z)]

und gibt:

['c', 'd', 'f']
Massifox
quelle
0

Das wird dir helfen.

def test_fun(val):
    x = 0
    y = 1
    z = 2
    myList = []
    if val in (x, y, z) and val == 0:
        myList.append("C")
    if val in (x, y, z) and val == 1:
        myList.append("D")
    if val in (x, y, z) and val == 2:
        myList.append("E")

test_fun(2);
Mayur
quelle
0

Sie können dies vereinen

x = 0
y = 1
z = 3

in einer Variablen.

In [1]: xyz = (0,1,3,) 
In [2]: mylist = []

Ändern Sie unsere Bedingungen wie folgt:

In [3]: if 0 in xyz: 
    ...:     mylist.append("c") 
    ...: if 1 in xyz: 
    ...:     mylist.append("d") 
    ...: if 2 in xyz: 
    ...:     mylist.append("e") 
    ...: if 3 in xyz:  
    ...:     mylist.append("f") 

Ausgabe:

In [21]: mylist                                                                                
Out[21]: ['c', 'd', 'f']
Serhii
quelle
0

Problem

Während das Muster zum Testen mehrerer Werte

>>> 2 in {1, 2, 3}
True
>>> 5 in {1, 2, 3}
False

ist sehr gut lesbar und funktioniert in vielen Situationen, es gibt eine Falle:

>>> 0 in {True, False}
True

Aber wir wollen haben

>>> (0 is True) or (0 is False)
False

Lösung

Eine Verallgemeinerung des vorherigen Ausdrucks basiert auf der Antwort von ytpillai :

>>> any([0 is True, 0 is False])
False

was geschrieben werden kann als

>>> any(0 is item for item in (True, False))
False

Obwohl dieser Ausdruck das richtige Ergebnis zurückgibt, ist er nicht so lesbar wie der erste Ausdruck :-(

fhgd
quelle