Seite 1 von 1

Mehrere Bedinungen in if-Verzweigung

Verfasst: Donnerstag 22. September 2022, 13:35
von PyFlo
Hallo zusammen,
ich habe eine grundlegende Frage zu if-Verzweigungen mit mehreren Bedingungen.
Und zwar möchte ich im folgenden Beispiel überprüfen, ob mehrere Zahlen (x, y und z) in einer bestimmten Range liegen.
Wenn ich den Code wie folgend schreibe, wird aber nur z in der Range überprüft.

Code: Alles auswählen

x, y, z = 25, 9, 19

if x and y and z in range(0,20):
    print("True")
else:
    print("False")
Muss ich in diesem Beispiel dann jede Variable auf die Range anwenden, also:

Code: Alles auswählen

if x in range(0,20) and y in range(0,20) and z in range(0,20):
oder gibt es hierfür eine "bessere" Lösung.

Vielen Dank im Voraus

Re: Mehrere Bedinungen in if-Verzweigung

Verfasst: Donnerstag 22. September 2022, 13:46
von PyFlo
Alternativ könnte man die Zahlen ja auch in eine Liste packen und diese über eine for-Schleife durchlaufen, also:

Code: Alles auswählen

x, y, z = 25, 9, 19
numbers = [x, y, z]
for number in numbers:
	if number in range(0, 20):
		print("True")
	else:
		print("False")

Re: Mehrere Bedinungen in if-Verzweigung

Verfasst: Donnerstag 22. September 2022, 13:54
von Sirius3
Beim Beispiel `x and y and z in range(0,20)` wird zuerst `x` geprüft, ob das "wahr" ist, also hier != 0. Wenn das der Fall ist (bei and kann man schon beim ersten False abbrechen) wird geprüft ob y wahr ist und falls ja, wird geprüft ob z eine ganze Zahl zwischen 0 und 20 ist.

Für Dein konkretes Problem gibt es viele mögliche Lösungen. Falls x, y und z immer Ganzahlen sind, würde ich das vorschlagen:

Code: Alles auswählen

if 0 <= min(x, y, z) and max(x, y, z) < 20:

Re: Mehrere Bedinungen in if-Verzweigung

Verfasst: Donnerstag 22. September 2022, 14:32
von narpfel
Oder auch mit `all`:

Code: Alles auswählen

if all(number in range(20) for number in numbers):
    print("all in range")

Re: Mehrere Bedinungen in if-Verzweigung

Verfasst: Donnerstag 22. September 2022, 15:24
von PyFlo
@Sirius3, @narpfel: Vielen Dank für eure Lösungsvorschläge!

Re: Mehrere Bedinungen in if-Verzweigung

Verfasst: Donnerstag 22. September 2022, 16:46
von pillmuncher
Noch besser, weil O(n) statt O(n²):

Code: Alles auswählen

set(numbers).issubset(range(20))
Ungetestet.

Siehe auch hier: https://docs.python.org/3/library/stdty ... t.issubset

Re: Mehrere Bedinungen in if-Verzweigung

Verfasst: Donnerstag 22. September 2022, 16:55
von narpfel
@pillmuncher: `x in range(N)` ist O(1), damit ist mein und Sirius3s Lösungsvorschlag jeweils O(K) (wenn K die Länge von `numbers` ist), deiner ist allerdings O(K + N).

Re: Mehrere Bedinungen in if-Verzweigung

Verfasst: Donnerstag 22. September 2022, 18:13
von __blackjack__
@narpfel: Das ist AFAIK nicht garantiert. Ich kann mich da dunkel an eine Diskussion erinnern über eine Python-Implementierung die beim `range()`-Ergebnis keine eigene `__contains__()`-Implementierung hat(te). Ich würde das mit ``0 <= value < 20`` testen.

Re: Mehrere Bedinungen in if-Verzweigung

Verfasst: Donnerstag 22. September 2022, 18:29
von snafu
Vielleicht noch zur Erklärung: Das range() erstellt ja seit Python 3.x keine Liste mehr, sondern ein spezielles range-Objekt. Dieses kennt sein Minimum und Maximum. Für Ganzzahlen (PyLong) ist der Code so optimiert, dass ein ``x in range(N)`` einem ``min(range(N)) <= x <= max(range(N))`` entspricht. Ein Set würde dieses Verhalten quasi kaputt machen. Die beiden performanteren Vorschläge übertragen den "Min-Max-Trick" nochmals auf die zu testenden Zahlen. Der Overhead für das Set entfällt dabei komplett. Daher kommt die entsprechend bessere Laufzeit zustande. An sich ist halt die Frage, warum man dafür überhaupt ein range-Objekt benutzt, wo man doch einfach direkt einen Vergleich mit dem Minimum und Maximum machen könnte...

Auszug aus rangeobject.c:

Code: Alles auswählen

/* Assumes (PyLong_CheckExact(ob) || PyBool_Check(ob)) */
static int
range_contains_long(rangeobject *r, PyObject *ob)
{
    PyObject *zero = _PyLong_GetZero();  // borrowed reference
    int cmp1, cmp2, cmp3;
    PyObject *tmp1 = NULL;
    PyObject *tmp2 = NULL;
    int result = -1;

    /* Check if the value can possibly be in the range. */

    cmp1 = PyObject_RichCompareBool(r->step, zero, Py_GT);
    if (cmp1 == -1)
        goto end;
    if (cmp1 == 1) { /* positive steps: start <= ob < stop */
        cmp2 = PyObject_RichCompareBool(r->start, ob, Py_LE);
        cmp3 = PyObject_RichCompareBool(ob, r->stop, Py_LT);
    }
    else { /* negative steps: stop < ob <= start */
        cmp2 = PyObject_RichCompareBool(ob, r->start, Py_LE);
        cmp3 = PyObject_RichCompareBool(r->stop, ob, Py_LT);
    }
   /* ... */
}
Genau genommen ist dem range-Objekt nicht das MInimum und Maximum bekannt, sondern lediglich die Werte für start und stop. Durch die Verzweigung auf Grundlage des step-Arguments läuft es aber darauf hinaus.

Re: Mehrere Bedinungen in if-Verzweigung

Verfasst: Donnerstag 22. September 2022, 18:30
von narpfel
@__blackjack__: Die Dokumentation ist da relativ klar:
https://docs.python.org/3/library/stdtypes.html#range hat geschrieben: Changed in version 3.2: [...] Test int objects for membership in constant time instead of iterating through all items.
Das liest sich für mich wie eine Garantie.

Re: Mehrere Bedinungen in if-Verzweigung

Verfasst: Donnerstag 22. September 2022, 18:48
von __blackjack__
@narpfel: Jo, aber erst sein Python 3.2. Die Diskussion an die ich mich erinnerte war zeitlich deutlich davor. Ich glaube das war so zu Python 2.5 Zeiten. 😎

Re: Mehrere Bedinungen in if-Verzweigung

Verfasst: Donnerstag 22. September 2022, 19:03
von snafu
Ich würde range() ja nur zum Iterieren benutzen. Bezüglich __contains__() erschließt sich mir der Vorteil da nicht wirklich.

Re: Mehrere Bedinungen in if-Verzweigung

Verfasst: Donnerstag 22. September 2022, 19:14
von narpfel
Ich finde `42 in range(100)` ist lesbarer als `0 <= 42 < 100`. So cool chained comparison operators auch sind, `42 in range(100)` ist für mich einfach näher an dem, was ich eigentlich ausdrücken möchte: „Ist 42 in diesem Intervall enthalten?“, nicht „Ist 0 kleinergleich als 42 und 42 kleiner als 100?“

Aber vielleicht bin auch einfach komisch. 🤷

Re: Mehrere Bedinungen in if-Verzweigung

Verfasst: Donnerstag 22. September 2022, 19:22
von Sirius3
@snafu: mit len, Index-Zugriff, `in` etc. verhält sich das `range`-Objekt in vielen Fällen wie eine Liste und kann so in Funktionen benutzt werden, die eigentlich eine Liste erwarten.

@narpfel: was erwartest Du bei `13.5 in range(100)`?

Re: Mehrere Bedinungen in if-Verzweigung

Verfasst: Donnerstag 22. September 2022, 19:25
von pillmuncher
Aha. Hab ich wieder was gelernt. Danke schön.

Tatsächlich finde ich

Code: Alles auswählen

if all(number for number in numbers if 0 <= number < 20)
am klarsten. Es wird genau ausgedrückt, was man haben möchte.

Re: Mehrere Bedinungen in if-Verzweigung

Verfasst: Donnerstag 22. September 2022, 19:33
von narpfel
@pillmuncher: Mit `numbers = [0]` funktioniert das nicht?

@Sirius3: `False`, weil `in range(N)` ein „ist eine der ersten N natürlichen Zahlen“ ist und `13.5` keine natürliche Zahl ist.

Re: Mehrere Bedinungen in if-Verzweigung

Verfasst: Donnerstag 22. September 2022, 19:35
von Sirius3
@pillermulcher: der Ausdruck ist fehlerhaft, denn wenn eine Zahl 0 ist, funktioniert das nicht.
Noch klarer und zusätzlich richtig ist es, wenn die Bedingung vorne steht:

Code: Alles auswählen

if all(0 <= number < 20 for number in numbers):

Re: Mehrere Bedinungen in if-Verzweigung

Verfasst: Donnerstag 22. September 2022, 19:38
von snafu
Ich finde, dass gerade der verkettete Vergleich das mögliche Vorhandensein von einer Zahl in einem Intervall sehr gut ausdrückt. Andererseits heißt Range halt Bereich und ist somit etwas "sprechender". Das ist wohl eine Geschmacksfrage, was man da lieber verwendet.

Dass sich ein range-Objekt im Sinne des Duck Typings möglichst wie eine Liste verhält, leuchtet mir nun ein.

Re: Mehrere Bedinungen in if-Verzweigung

Verfasst: Donnerstag 22. September 2022, 19:51
von snafu
narpfel hat geschrieben: Donnerstag 22. September 2022, 19:33 @Sirius3: `False`, weil `in range(N)` ein „ist eine der ersten N natürlichen Zahlen“ ist und `13.5` keine natürliche Zahl ist.
Und auch einfach, weil sich Sequenzen in Python so verhalten, dass kein TypeError geworfen wird (falls das der Gedanke dahinter war). Mit range() kriegt man quasi ein imitiertes Tupel (da unveränderbar), das auf Ganzzahlen beschränkt ist. Und die geben False für jedes Objekt (egal welchen Typs) zurück, wenn es nicht enthalten ist.

Re: Mehrere Bedinungen in if-Verzweigung

Verfasst: Donnerstag 22. September 2022, 20:05
von pillmuncher
Ja, mein Code war fehlerhaft. Ich sollte nicht neben dem Fernsehen programmieren. Der Code von Sirius3 ist der richtige:

Code: Alles auswählen

if all(0 <= number < 20 for number in numbers):