Verständnisfrage zu Bitweiser-Operatoren

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
Dennis89
User
Beiträge: 1738
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo zusammen,

bei mir tauchen immer wieder Fragen auf, wenn es um Bitweiser-Operatoren geht.
Wenn ich zum Beispiel folgendes ausführe, erhalte ich ein Ergebnis, das ich nur bedingt erwarte:

Code: Alles auswählen

>>>set([1, 2, 3, 4]) & set([7, 3, 6, 1])
{1, 3}
An sich erwarte ich schon, dass die 1 und die 3 geliefert werden. Allerdings frage ich mich, wie läuft der Prozess dahinter ab, bis die Bits, bildlich gesprochen, übereinander stehen und kombiniert werden können? Weil wenn ich jetzt jede Zahl in der geschriebenen Reihenfolge in Binär übersetze und die zwei Mengen untereinander schreibe, dann steht die 1 nicht unter der 1, sondern unter der 4. Ich habe dann sowas:

Code: Alles auswählen

0b1, 0b10, 0b11, 0b100
0b111, 0b11, 0b110, 0b1
Werden die Werte vor so einem Vergleich immer erst sortiert? Aufsteigend? Und wie ist das bei Strings?

Code: Alles auswählen

>>>set("a4bc") & set("cgb4")
{'c', 'b', '4'}
Zusammengefasst interessiert mich, was passiert intern, bis es zur Verknüpfung zu den Bits kommt?

Ich bin euch sehr dankbar für jede Antwort. Macht euch aber bitte nicht die Mühe, die Frage in ChatGPT zu tippen und die Antwort zu posten. Ich frage und bitte bewusst um menschliches Wissen.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
snafu
User
Beiträge: 6968
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Wie kommst du darauf, dass bei Sets bitweise Operationen zur Anwendung kommen? Wenn du in der Shell zwei Aufrufe mit einer Pipe verknüpfst, dann wird intern ja auch kein bitweises Oder auf die Rückgabewerte angewendet.
Benutzeravatar
pillmuncher
User
Beiträge: 1534
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Deine Verwirrung kommt daher, dass du ein unzutreffendes mentales Bild davon hast, was & und | bedeuten. Genauer gesagt, dass du ihre Anwendung auf Bitketten als fundamental anzusehen scheinst. Es gibt hier keine fundamentale Bedeutung, sondern nur strukturell ähnliche Verwendungsweisen in unterschiedlichen mathematischen Gebieten.

Tatsächlich sind & und | zunächst nur andere symbolische Namen für die logischen Operatoren∧und∨. Die Boolesche Algebra, auf der Bitketten-Berechnungen beruhen, bildet einen vollständingen Verband. ∧und∨ sind dabei die sog. join und meet Operatoren. Mengen bilden ebenfalls einen solchen vollständingen Verband. Die Operatoren aus der Mengenlehre sind der Vereinigungsoperator ∪ und der Schnittmengenoperator ∩. Die visuelle Ähnlichkeit zwischen ∩ und ∧auf der einen, und ∪ und ∨ auf der anderen Seite sind nicht zufällig gewählt. ab in der Logik bedeutet "sowohl a als auch b" und ab in der Mengenlehre bedeutet "die Menge der Elemente die sowohl in a als auch in b vorkommen". Analog dazu bedeutet ab "a oder b oder beides" und ab "die Menge der Elemente die in a oder b oder beiden vorkommen".

Wenn man die Namen ∧und∨ durch & und | ersetzt und auf ein einzelnes Paar von Bits anwendet, dann bekommt man zwei Wahrheitstabellen:

Code: Alles auswählen

A B | A & B
----+------
0 0 |   0
0 1 |   0
1 0 |   0
1 1 |   1

A B | A | B
----+------
0 0 |   0
0 1 |   1
1 0 |   1
1 1 |   1
Wenn man logische Operatoren verwendet, dann sieht das so aus:

Code: Alles auswählen

P Q | P ∧ Q
----+-------
⊥ ⊥ |   ⊥
⊥ ⊤ |   ⊥
⊤ ⊥ |   ⊥
⊤ ⊤ |   ⊤

P Q | P ∨ Q
----+-------
⊥ ⊥ |   ⊥
⊥ ⊤ |   ⊤
⊤ ⊥ |   ⊤
⊤ ⊤ |   ⊤
Die typographischen Namen für ⊤ und ⊥ sind Down Tack und Up Tack, in der Logik True und False oder Verum und Falsum, in der Verbandstheorie Top und Bottom, und in der Ordnungstheorie (Verbände sind Ordnungen) Supremum und Infimum.

Wie kommt man nun zu den Operationen auf Bitketten? Das ist einfach die paarweise Anwendung derselben Operation auf zwei "zipped" Bitketten:

Code: Alles auswählen

  0b101 
& 0b110
-------
  0b100 

  0b101 
| 0b110
-------
  0b111
Wenn die Bitketten Python-Listen wären, könnte man schreiben:

Code: Alles auswählen

>>> [a & b for a, b in zip([1, 0, 1], [1, 1, 0])]
[1, 0, 0]
>>> [a | b for a, b in zip([1, 0, 1], [1, 1, 0])]
[1, 1, 1]
Das lässt sich nicht eins zu eins auf die Mengenlehre übertragen, weil Elemente in einer Menge nicht ordinal geordnet sind. Man kann von einer Menge Zahlen sagen, dass es eine kleinste oder größte gibt, oder dass von zwei Zahlen eine größer oder kleiner als die andere ist, aber es gibt keine "erste" oder "letzte" oder "mittlere" Zahl, weswegen man zip hier nicht einfach so anwenden kann. Statt dessen bildet Python eine neue Menge, die alle Elemente beider Mengen enthält, oder testet welche Elemente in beiden Mengen vorkommen und bildet daraus dann eine neue Menge.


Disclaimer: Ja, die Tabellen habe ich von ChatGPT zeichnen lassen, weil ich zu faul war, das selbst zu tun. Alles andere ist von mir.
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
snafu
User
Beiträge: 6968
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@Dennis89: Hier findet man übrigens die passende Stelle in der Doku zu den verschiedenen Set-Operationen:

https://docs.python.org/3/library/stdtypes.html#set

Es werden immer zuerst die ausgeschriebenen Methodennamen genannt und direkt darunter der Operator, der jeweils als Alias dafür verwendet wird. Anschließend erfolgt die Beschreibung der Methode. Niemand spricht dort von bitweisen Operationen.

Ich würde die Zeichen auch anders als du in erster Linie als logische Operatoren ansehen. Die bitweisen Operatoren sind einfach ein Spezialfall davon, auch wenn dir vielleicht bisher keine andere Verwendung begegnet ist. Es sind in dem Sinne ja ebenso logische Operationen, nur dass die auf jedes einzelne Bit angewendet werden, wie ja bereits anschaulich erklärt wurde.
Pedroski55
User
Beiträge: 55
Registriert: Freitag 25. Juli 2025, 00:20

bei mir tauchen immer wieder Fragen auf, wenn es um Bitweiser-Operatoren geht.
Vielleicht hilft das hier:

Code: Alles auswählen

"""
AND  & True if both are True
OR  | True if one or both are True
NOT  ~ Bitwise inversion 1 becomes 0, 0 becomes 1
XOR  ^ True if one is True and other is False
LEFT-SHIFT  << x<< y moves all values in x y places to the left
RIGHT-SHIFT  >> x >> y moves all values in x y places to the left
"""

a = [1, 2, 3, 4]
b = [7, 3, 6, 1]
abin = [format(num, '08b') for num in a] # ['00000001', '00000010', '00000011', '00000100']
bbin = [format(num, '08b') for num in b] # ['00000111', '00000011', '00000110', '00000001']

for i in range(len(abin)):
    print(f'abin[{i}] = {abin[i]}')
    print(f'bbin[{i}] = {bbin[i]}')
    num1 = a[i] & b[i]    
    print(f'AND = {format(num1, "08b")}')  
    num2 = a[i] | b[i]
    print(f'OR = {format(num2, "08b")}')
    print('********')

# LEFT-SHIFT
x = 0b00000101 # 5
y = x << 2  # 20
z = bin(y) # '0b10100'
Benutzeravatar
__blackjack__
User
Beiträge: 14385
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Pedroski55: Du erklärst hier die bitweisen Operatoren — gefragt war aber wie die bei den gezeigten Mengenoperationen verwendet werden. Und die Antwort wurde ja auch schon gegeben: gar nicht.

``for i in range(len(sequence))`` ist ein „anti pattern“ in Python. Man kann direkt über die Elemente von Sequenzen iterieren, ohne den unnötigen Zwischenschritt über einen Laufindex. Sollte man _zusätzlich_ zu den Elementen eine laufende Zahl benötigen gibt es `enumerate()`. Um über die Elemente von mehr als einer Sequenz ”parallel” zu iterieren gibt es `zip()`.

`format()` _innerhalb_ einer f-Zeichenkette zu verwenden ist unsinnig. Die Funktionalität hat man doch bereits durch die f-Zeichenkette. Das ”vorberechnen” von `abin` und `bbin` macht das hier auch nur unnötig komplizierter als es sein müsste.

Code: Alles auswählen

#!/usr/bin/env python3
for a, b in zip([1, 2, 3, 4], [7, 3, 6, 1]):
    print(f"  a = {a:04b}")
    print(f"  b = {b:04b}")
    print(f"AND = {a & b:04b}")
    print(f" OR = {a | b:04b}")
    print(f"XOR = {a ^ b:04b}")
    print("**********")
Who is General Failure and why is he reading my hard disk?
Benutzeravatar
snafu
User
Beiträge: 6968
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@Pedroski55: Und es ist nicht nur der unnötige Zwischenschritt für den Indexzugriff, sondern auch fehleranfällig, falls man Zahlen ändert und versehentlich die Liste a länger als die Liste b macht. Dann wirft das nämlich einen IndexError und den auch erst nach Ausgabe des letzten möglichen Zahlenpaars. Ist hier bei dem Beispielcode nicht ganz so wild, aber innerhalb eines größeren komplexeren Programms durchaus nervig. zip() hingegen würde einfach alle Zahlen, die zu viel sind, ignorieren und dann keine weiteren Paare liefern.

Anbei der Code in Rust (einfach weil ich Lust drauf hatte). Liegt für diese recht einfache Aufgabe irgendwo zwischen Python und C. Wobei Rust auch zip() kennt, wenn auch nur als Methode der Iterator API und leider auch immer nur für ein Argument statt beliebig vielen wie in Python.

Code: Alles auswählen

fn main() {
    let vec_a = [1, 2, 3, 4];
    let vec_b = [7, 3, 6, 1];

    for (a, b) in vec_a.into_iter().zip(vec_b) {
        println!("a   = {:04b}", a);
        println!("b   = {:04b}", b);
        println!("AND = {:04b}", a & b);
        println!("OR  = {:04b}", a | b);
        println!("XOR = {:04b}", a ^ b);
        println!("**********");
    }
}
nezzcarth
User
Beiträge: 1804
Registriert: Samstag 16. April 2011, 12:47

Dennis89 hat geschrieben: Mittwoch 22. April 2026, 10:36 Zusammengefasst interessiert mich, was passiert intern, bis es zur Verknüpfung zu den Bits kommt?
Zusammengefasst kann man sagen, dass & und | hier effektiv keine bitweisen Operatoren sind, sondern Mengenoperatoren. Ducktyping und so:

Code: Alles auswählen

In [3]: {3,5}.__or__({5,6})
Out[3]: {3, 5, 6}

In [4]: {3,5}.__xor__({5,6})
Out[4]: {3, 6}

In [5]: {3,5}.__and__({5,6})
Out[5]: {5}

In [6]: {3,5}.__sub__({5,6})
Out[6]: {3}

In [7]: {3,5} | {5,6}
Out[7]: {3, 5, 6}

In [8]: {3,5} ^ {5,6}
Out[8]: {3, 6}

In [9]: {3,5} & {5,6}
Out[9]: {5}

In [10]: {3,5} - {5,6}
Out[10]: {3}

Benutzeravatar
Dennis89
User
Beiträge: 1738
Registriert: Freitag 11. Dezember 2020, 15:13

Guten Morgen,

super, vielen Dank für eure (sehr detaillierten) Erklärungen! Das war mir so nicht bewusst, wie ihr richtig erkannt habt. Zu den einzelnen Beiträgen, habe ich auch keine weiteren Fragen mehr, wurde sehr gut erklärt.


Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
__blackjack__
User
Beiträge: 14385
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Auch wenn keine Fragen mehr sind, hätte ich trotzdem noch eine Erklärung. :-) Nämlich wo sich die beiden Themen Mengenoperationen und bitweise Operationen tatsächlich treffen können: Wenn man eine feste Grundmenge an Elementen hat, kann man jedem Element eine Bitposition in einer Zahl zuordnen, die dann bedeutet das Element ist in der Menge enthalten (1) oder nicht enthalten (0). Wenn man das so kodiert, dann entsprechen die Operatoren ``&`` und ``|`` bitweise und was die Menge(n) angeht einander.

In Python findet man das beispielsweise in der Standardbibliothek im `enum`-Modul mit dem `IntFlag`-Typ. Mal als typisches Leer-/Handbuchbeispiel die Wochentage als Menge:

Code: Alles auswählen

#!/usr/bin/env python3
from enum import auto, IntFlag


class Days(IntFlag):
    NONE = 0
    MON = auto()
    TUE = auto()
    WED = auto()
    THU = auto()
    FRI = auto()
    SAT = auto()
    SUN = auto()


def print_days(label, value):
    print(f"{label:>5} = {value:08b} = {value!r}")


def main():
    for day in Days:
        print_days(day.name, day)

    all_days = ~Days.NONE
    print_days("all", all_days)

    days_a = Days.MON | Days.SAT | Days.SUN
    days_b = Days.WED | Days.SAT
    print_days("a", days_a)
    print_days("b", days_b)
    print_days("a | b", days_a | days_b)
    print_days("a & b", days_a & days_b)
    print_days("a ^ b", days_a ^ days_b)


if __name__ == "__main__":
    main()
Ausgabe:

Code: Alles auswählen

  MON = 00000001 = <Days.MON: 1>
  TUE = 00000010 = <Days.TUE: 2>
  WED = 00000100 = <Days.WED: 4>
  THU = 00001000 = <Days.THU: 8>
  FRI = 00010000 = <Days.FRI: 16>
  SAT = 00100000 = <Days.SAT: 32>
  SUN = 01000000 = <Days.SUN: 64>
  all = 01111111 = <Days.MON|TUE|WED|THU|FRI|SAT|SUN: 127>
    a = 01100001 = <Days.MON|SAT|SUN: 97>
    b = 00100100 = <Days.WED|SAT: 36>
a | b = 01100101 = <Days.MON|WED|SAT|SUN: 101>
a & b = 00100000 = <Days.SAT: 32>
a ^ b = 01000101 = <Days.MON|WED|SUN: 69>
Bei Programmiersprachen die keine spezielle Unterstützung für so etwas haben, macht man das oft einfach manuell mit Zahlen, Konstanten, und bitweisen Operationen. In C beispielsweise.

Es gibt aber auch Sprachen bei denen so etwas direkt als Datentyp eingebaut ist. In Pascal kann man Mengentypen von jedem aufzählbaren Typen oder Bereiche davon erstellen, mit bis zu 256 Elementen in der Grundmenge, die dann mit bis zu 32 Bytes gespeichert werden.
Who is General Failure and why is he reading my hard disk?
Sirius3
User
Beiträge: 18403
Registriert: Sonntag 21. Oktober 2012, 17:20

Man kann auch umgekehrt bit-Operationen als Mengen ausdrücken:

Code: Alles auswählen

from enum import auto, IntFlag

class Bits(IntFlag):
    NONE = 0
    BIT0 = auto()
    BIT1 = auto()
    BIT2 = auto()
    BIT3 = auto()
    BIT4 = auto()
    BIT5 = auto()
    BIT6 = auto()
    BIT7 = auto()

num84 = set(Bits(84))
num107 = set(Bits(107))
print(num84)
print(num107)
print(num107 & num84)
print(num107 | num84)
print(sum(num107 | num84))
Pedroski55
User
Beiträge: 55
Registriert: Freitag 25. Juli 2025, 00:20

Kinder Kinder, warum habt ihr denn nicht im Python Kindergarten aufgepasst?

Fürchtet euch nicht Kinder, Pedroski wird euch unter die Arme greifen! Fix aufpassen ja? Nicht rumspielen!
for i in range(len(sequence))`` ist ein „anti pattern“ in Python.
Ihr seid sehr schlau Kinder! Ihr merkt euch schon Fremdworter! Was wohl ist „anti pattern“? Irgendwie etwas, das wir nicht mögen? Ja! Nieder damit!
anti pattern: in Computersprache nicht genau definiert aber in etwa: eine Lösung für ein Problem, die dann hinterher mehr Probleme verursacht.
Damit ist ausgeschlossen, dass range(len(liste)) immer ein anti pattern ist. Falls kein: IndexError: list index out of range, kein Problem.

Code: Alles auswählen

nums = [1, 2, 3, 4, 5]
type(len(nums)) # <class 'int'>
Natürlich Kinder, wenn man an der Länge der Liste während des Ablaufs der Schleife tüftelt, nun das wäre nicht so schön. Bitte davon Abstand halten meine Braven!
Man kann direkt über die Elemente von Sequenzen iterieren, ohne den unnötigen Zwischenschritt über einen Laufindex.
Man kann in Python gar nichts direkt machen. Hinter den Kulissen läuft immer mehr ab. Alles ist ein Class, mit Class Methoden.

Code: Alles auswählen

nums = [1, 2, 3, 4, 5]
for num in nums:
    print(num)
Was der böse Python mit dem obigen Code eigentlich macht ist etwa folgendes:

Code: Alles auswählen

it = iter(nums)
while True:
    try:
        num = next(it)
        print(num)
    except StopIteration:
        break
Wer kann mir nun diese Frage beantworten: Warum hat der böse Python Listen Indexnummern gegeben?

Ja, Kurt, du Schlaumeier? "Damit wir sie nicht benutzen!"
Nicht 100% richtig Kurt, aber danke dafür!
sequence: An iterable which supports efficient element access using integer indices via the __getitem__() special method and defines a __len__() method that returns the length of the sequence.
Merkt euch das ausländische Wort "efficient" Kinder. Was könnte das wohl auf Deutsch bedeuten?

Code: Alles auswählen

nums = [1, 2, 3, 4, 5]
smun = [5, 4, 3, 2, 1]
for i in range(len(nums)):
    print(nums[i], smun[i])
Jaja Kinder, auch heute gibt es Hausaufgaben: Versucht mal eine Funktion zu schreiben, die die Elemente zweier Listen paarweise zusammenfügt.

Dabei stellt euch mal vor, was die Funktion zip() hinter den Kulissen eigentlich tut.

Code: Alles auswählen

def zip_it(liste1, liste2):
    pass
Merkt euch: ihr werdet die Längen der Listen vergleichen müssen und eine Entscheidung treffen müssen. Bis Morgen meine Lieben!
Benutzeravatar
sparrow
User
Beiträge: 4652
Registriert: Freitag 17. April 2009, 10:28

@Pedroski55: Ich weiß nicht, woher du die Definition von "Anti Pattern" hast. Schau doch mal in die Wikipedia, dort wird das ganz gut erklärt und ist nicht das, was du da zitierst.

Der Rest deines Beitrages zeigt in meinen Augen, dass du die Ausführungen nicht verstanden hast. Das was du so ausufernd und Personen abwertend schreibst, hat gar nichts damit zu tun, was du in deinem Code falsch machst.

Eine Möglichkeit wäre es, die Kritik als Möglichkeit zu verstehen, wie du besseren Code schreiben kannst.
Benutzeravatar
snafu
User
Beiträge: 6968
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@Pedroski55: Tja, das haben Funktionsaufrufe wohl so an sich, dass intern etwas passiert. Vielen Dank für diese bahnbrechende Erleuchtung.

Im Übrigen sind deine Erklärungen sehr stark auf den konkreten Einzelfall verkürzt worden. Eine for-Schleife wie auch die zip()-Funktion arbeiten unter der Haube immer mit dem Iterator Protokoll, also mit dem, was für das konkrete Objekt als nächstes Element definiert worden ist. Den zugehörigen Ablauf samt next()-Aufruf hattest du ja zumindest für die Schleife bereits korrekt in deinen Beitrag kopiert.

Das kann im Endeffekt ``items[ i ]`` sein, muss es aber nicht. So kann man in zip() zum Beispiel einen potenziell unendlich lange laufenden Iterator wie etwa count() aus dem itertools-Modul mit einem endlich laufenden Objekt (z. B. einer Liste) kombinieren und hätte sich damit enumerate() nachgebaut, weil zip() per Definition endet, sobald einer der Iteratoren aufgebraucht wurde (also technisch gesehen eine StopIteration als Ausnahme geworfen hat).

Und es geht letztlich ja gerade darum, Werkzeuge zu nutzen, um sich das Leben einfacher, sicherer, effizienter und dergleichen zu machen. Erzählst du einem Handwerker mit Akkuschrauber auch, dass er das Gleiche mit einem Schraubendreher erreichen könnte und erklärst ihm ungefragt die interne Funktionsweise des Schraubers? :)
Benutzeravatar
Dennis89
User
Beiträge: 1738
Registriert: Freitag 11. Dezember 2020, 15:13

@__blackjack__ und @Sirius3 Danke für eure Ergänzung!

Da das Thema hier zum Troll-Spielplatz geworden ist, bin ich mal weg.
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
noisefloor
User
Beiträge: 4322
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Da das Thema hier zum Troll-Spielplatz geworden ist, bin ich mal weg.
Nee, es gibt halt nur Leute, die gerne öfters sehr simple Lösungen präsentieren und dann aber nicht akzeptieren können, dass es an vielen Stelle irgendwo zwischen besser so nicht / unpythonisch / Müll ist. Das Verhaltensmuster ist übrigens bei python-forum.io genau so. Da wurde u.a. DeaD_EyE mehrfach ziemlich von dem betreffenden Troll beleidigt. Tipp: einfach ignorieren und bei Lesen des Threads übersrpringen. Was mich bei so was immer beruhigt: so Posts mit schlechten Code tragen ja aktiv zur Verblödung von Chatbots bei, die das Forum hier crawlen, weil das ja quasi negatives Lernen ist. Hat so gesehen auch was positives.

Wenn jemand null Motivation hat, weiter zu lernen und besser zu werden: sein Problem. Was dann auch zu der spannenden Frage führt: wenn jemand so beratungsresistent ist, dass die Person nicht mehr merkt, dass sie beratungsresistent ist -> ist sie dann nicht mehr beratungsresistent?

Gruß, noisefloor
Benutzeravatar
__blackjack__
User
Beiträge: 14385
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Pedroski55: Indexzugriff auf Sequenzen ist in der Regel nicht effizienter als über die Sequenz zu iterieren. Denn beim _zusätzlichen Umweg_ über Indexzugriff hat man neben der Sequenz, über die man iterieren will, noch die Sequenz mit den Indexwerten über die man iteriert um sie dann für den Zugriff zu verwenden.

Der Indexzugriff ist natürlich zum benutzen da, falls man den braucht. Nämlich dann wenn man gezielt und wahlfrei auf einzelne Werte der Sequenz zugreifen muss, statt nacheinander in der Reihenfolge der Sequenz darauf zuzugreifen.

Neben der schlechteren Effizienz beim Iterieren ist ein weiterer Nachteil von dem „anti pattern“, dass man an sehr vielen Stellen mehr von dem Objekt erwartet als eigentlich nötig ist. Also dass das dann tatsächlich nur mit einer Sequenz funktioniert, statt mit jedem beliebigen iterierbaren Objekt, und man dann gegebenfalls gezwungen ist iterierbare Objekte vorher in Sequenzen zu wandeln und dafür zusätzlich Rechenzeit und Speicherverbrauch zu benötigen.

Die `zip()`-Funktion iteriert hinter den Kulissen _nicht_ über einen Index und verwendet auch kein `len()`, denn `zip()` funktioniert mit beliebigen iterierbaren Objekten, also auch solche die gar keinen Indexzugriff und keine abfragbare Länge haben.

Die Funktion sieht dann grob so aus (ungetestet):

Code: Alles auswählen

def zip_it(iterable_a, iterable_b):
    iterator_a = iter(iterable_a)
    iterator_b = iter(iterable_b)
    try:
        while True:
            yield (next(iterator_a), next(iterator_b))
    except StopIteration:
        pass
Die Argumentation „Python kann X nicht, weil hinter den Kulissen …“ ist unsinnig. Nach dieser Argumentation könnte keine Programmiersprache ausdrucksstärker als Maschinensprache sein, denn letztlich muss das hinter den Kulissen alles auf einem Prozessor in Maschinensprache laufen. Von der Laufzeit gesehen könnte man in Maschinensprache in der Regel effizienteren Code schreiben, als das was in Hochsprachen dann letztlich gemacht wird. Es wäre aber ein „anti pattern“ einfach alles in Maschinensprache zu schreiben. :-)
Who is General Failure and why is he reading my hard disk?
Antworten