Grundlagen von Python-Sequenzen

Intermediate

This tutorial is from open-source community. Access the source code

Einführung

Python Sequenzen sind geordnete Sammlungen von Elementen. Sie werden durch ganze Zahlen indiziert.

Dies ist ein Guided Lab, das schrittweise Anweisungen bietet, um Ihnen beim Lernen und Üben zu helfen. Befolgen Sie die Anweisungen sorgfältig, um jeden Schritt abzuschließen und praktische Erfahrungen zu sammeln. Historische Daten zeigen, dass dies ein Labor der Stufe Fortgeschrittener mit einer Abschlussquote von 80% ist. Es hat eine positive Bewertungsrate von 100% von den Lernenden erhalten.

Sequenzdatentypen

Python hat drei Sequenz-Datentypen.

  • String: 'Hello'. Ein String ist eine Sequenz von Zeichen.
  • Liste: [1, 4, 5].
  • Tupel: ('GOOG', 100, 490.1).

Alle Sequenzen sind geordnet, durch ganze Zahlen indiziert und haben eine Länge.

a = 'Hello'               ## String
b = [1, 4, 5]             ## Liste
c = ('GOOG', 100, 490.1)  ## Tupel

## Indizierte Reihenfolge
a[0]                      ## 'H'
b[-1]                     ## 5
c[1]                      ## 100

## Länge der Sequenz
len(a)                    ## 5
len(b)                    ## 3
len(c)                    ## 3

Sequenzen können repliziert werden: s * n.

>>> a = 'Hello'
>>> a * 3
'HelloHelloHello'
>>> b = [1, 2, 3]
>>> b * 2
[1, 2, 3, 1, 2, 3]
>>>

Sequenzen vom gleichen Typ können konkateniert werden: s + t.

>>> a = (1, 2, 3)
>>> b = (4, 5)
>>> a + b
(1, 2, 3, 4, 5)
>>>
>>> c = [1, 5]
>>> a + c
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate tuple (not "list") to tuple

Slicing

Slicing bedeutet, eine Teilfolge aus einer Sequenz zu extrahieren. Die Syntax lautet s[start:end]. Dabei sind start und end die Indizes der Teilfolge, die Sie möchten.

a = [0,1,2,3,4,5,6,7,8]

a[2:5]    ## [2,3,4]
a[-5:]    ## [4,5,6,7,8]
a[:3]     ## [0,1,2]
  • Die Indizes start und end müssen ganze Zahlen sein.
  • Slices enthalten den Endwert nicht. Es ist wie ein halboffenes Intervall aus der Mathematik.
  • Wenn die Indizes weggelassen werden, nehmen sie standardmäßig den Anfang oder das Ende der Liste an.

Slice-Zuweisung neu

Bei Listen können Slices neu zugewiesen und gelöscht werden.

## Neuzuweisung
a = [0,1,2,3,4,5,6,7,8]
a[2:4] = [10,11,12]       ## [0,1,10,11,12,4,5,6,7,8]

Hinweis: Der neu zugewiesene Slice muss nicht die gleiche Länge haben.

## Löschung
a = [0,1,2,3,4,5,6,7,8]
del a[2:4]                ## [0,1,4,5,6,7,8]

Sequenzreduzierungen

Es gibt einige häufige Funktionen, um eine Sequenz auf einen einzelnen Wert zu reduzieren.

>>> s = [1, 2, 3, 4]
>>> sum(s)
10
>>> min(s)
1
>>> max(s)
4
>>> t = ['Hello', 'World']
>>> max(t)
'World'
>>>

Iteration über eine Sequenz

Die for-Schleife iteriert über die Elemente in einer Sequenz.

>>> s = [1, 4, 9, 16]
>>> for i in s:
...     print(i)
...
1
4
9
16
>>>

Bei jeder Iteration der Schleife erhalten Sie ein neues Element, mit dem Sie arbeiten können. Dieser neue Wert wird in die Iterationsvariable eingefügt. In diesem Beispiel ist die Iterationsvariable x:

for x in s:         ## `x` ist eine Iterationsvariable
  ...statements

Bei jeder Iteration wird der vorherige Wert der Iterationsvariable überschrieben (sofern vorhanden). Nachdem die Schleife beendet ist, behält die Variable den letzten Wert bei.

break-Anweisung

Sie können die break-Anweisung verwenden, um frühzeitig aus einer Schleife auszubrechen.

for name in namelist:
    if name == 'Jake':
        break
 ...
 ...
statements

Wenn die break-Anweisung ausgeführt wird, tritt sie aus der Schleife aus und springt zu den nächsten statements. Die break-Anweisung betrifft nur die innerste Schleife. Wenn diese Schleife innerhalb einer anderen Schleife befindet, wird die äußere Schleife nicht verlassen.

continue-Anweisung

Um ein Element zu überspringen und zum nächsten zu gelangen, verwenden Sie die continue-Anweisung.

for line in lines:
    if line == '\n':    ## Überspringe leere Zeilen
        continue
    ## Weitere Anweisungen
 ...

Dies ist nützlich, wenn das aktuelle Element nicht von Interesse ist oder bei der Verarbeitung ignoriert werden muss.

Schleifen über ganze Zahlen

Wenn Sie zählen müssen, verwenden Sie range().

for i in range(100):
    ## i = 0,1,...,99

Die Syntax lautet range([start,] end [,step])

for i in range(100):
    ## i = 0,1,...,99
for j in range(10,20):
    ## j = 10,11,..., 19
for k in range(10,50,2):
    ## k = 10,12,...,48
    ## Beachten Sie, wie es in Schritten von 2 zählt, nicht 1.
  • Der Endwert wird niemals eingeschlossen. Es spiegelt das Verhalten von Slices wider.
  • start ist optional. Standardwert 0.
  • step ist optional. Standardwert 1.
  • range() berechnet Werte nach Bedarf. Es speichert tatsächlich keine großen Zahlenbereiche.

enumerate()-Funktion

Die enumerate-Funktion fügt einen zusätzlichen Zählerwert zur Iteration hinzu.

names = ['Elwood', 'Jake', 'Curtis']
for i, name in enumerate(names):
    ## Schleifen mit i = 0, name = 'Elwood'
    ## i = 1, name = 'Jake'
    ## i = 2, name = 'Curtis'

Die allgemeine Form lautet enumerate(sequence [, start = 0]). start ist optional. Ein gutes Beispiel für die Verwendung von enumerate() ist das Verfolgen der Zeilennummern beim Lesen einer Datei:

with open(filename) as f:
    for lineno, line in enumerate(f, start=1):
     ...

Am Ende ist enumerate einfach eine schöne Abkürzung für:

i = 0
for x in s:
    statements
    i += 1

Das Verwenden von enumerate erfordert weniger Tipparbeit und läuft etwas schneller.

For-Schleifen und Tupel

Sie können mit mehreren Iterationsvariablen iterieren.

points = [
  (1, 4),(10, 40),(23, 14),(5, 6),(7, 8)
]
for x, y in points:
    ## Schleifen mit x = 1, y = 4
    ##            x = 10, y = 40
    ##            x = 23, y = 14
    ##           ...

Wenn Sie mehrere Variablen verwenden, wird jedes Tupel in eine Reihe von Iterationsvariablen aufgelöst. Die Anzahl der Variablen muss der Anzahl der Elemente in jedem Tupel entsprechen.

zip()-Funktion

Die zip-Funktion nimmt mehrere Sequenzen entgegen und erstellt einen Iterator, der sie kombiniert.

columns = ['name','shares', 'price']
values = ['GOOG', 100, 490.1 ]
pairs = zip(columns, values)
## ('name','GOOG'), ('shares',100), ('price',490.1)

Um das Ergebnis zu erhalten, müssen Sie iterieren. Sie können wie zuvor mehrere Variablen verwenden, um die Tupel aufzulösen.

for column, value in pairs:
  ...

Eine häufige Verwendung von zip ist das Erstellen von Schlüssel/Wert-Paaren zum Erstellen von Dictionaries.

d = dict(zip(columns, values))

Übung 2.13: Zählen

Versuchen Sie einige grundlegende Zählbeispiele:

>>> for n in range(10):            ## Zählen von 0... 9
        print(n, end=' ')

0 1 2 3 4 5 6 7 8 9
>>> for n in range(10,0,-1):       ## Zählen von 10... 1
        print(n, end=' ')

10 9 8 7 6 5 4 3 2 1
>>> for n in range(0,10,2):        ## Zählen von 0, 2,... 8
        print(n, end=' ')

0 2 4 6 8
>>>

Übung 2.14: Weitere Sequenzoperationen

Experimentieren Sie interaktiv mit einigen der Sequenzreduktionsoperationen.

>>> data = [4, 9, 1, 25, 16, 100, 49]
>>> min(data)
1
>>> max(data)
100
>>> sum(data)
204
>>>

Versuchen Sie, über die Daten zu iterieren.

>>> for x in data:
        print(x)

4
9
...
>>> for n, x in enumerate(data):
        print(n, x)

0 4
1 9
2 1
...
>>>

Manchmal wird die for-Anweisung, len() und range() von Neulingen in einem schrecklichen Codefragment verwendet, das so aussieht, als wäre es aus der Tiefe eines rostigen C-Programms gekommen.

>>> for n in range(len(data)):
        print(data[n])

4
9
1
...
>>>

Machen Sie das nicht! Nicht nur macht es jedem die Augen weh, es ist auch ineffizient bei der Arbeitsspeicherverwaltung und läuft viel langsamer. Verwenden Sie einfach eine normale for-Schleife, wenn Sie über Daten iterieren möchten. Verwenden Sie enumerate(), wenn Sie aus irgendeinem Grund den Index benötigen.

Übung 2.15: Ein praktisches Beispiel für enumerate()

Denken Sie daran, dass die Datei missing.csv Daten eines Aktienportfolios enthält, aber einige Zeilen fehlende Daten haben. Verwenden Sie enumerate(), um Ihr pcost.py-Programm so zu modifizieren, dass es eine Zeilennummer mit der Warnmeldung ausgibt, wenn es auf schlechtes Eingabedata stößt.

>>> cost = portfolio_cost('/home/labex/project/missing.csv')
Zeile 4: Konvertieren nicht möglich: ['MSFT', '', '51.23']
Zeile 7: Konvertieren nicht möglich: ['IBM', '', '70.44']
>>>

Um dies zu tun, müssen Sie einige Teile Ihres Codes ändern.

...
for rowno, row in enumerate(rows, start=1):
    try:
     ...
    except ValueError:
        print(f'Zeile {rowno}: Schlechte Zeile: {row}')

Übung 2.16: Verwendung der zip()-Funktion

In der Datei portfolio.csv enthält die erste Zeile die Spaltenüberschriften. In all unserem bisherigen Code haben wir sie ignoriert.

>>> f = open('/home/labex/project/portfolio.csv')
>>> rows = csv.reader(f)
>>> headers = next(rows)
>>> headers
['name','shares', 'price']
>>>

Was passiert aber, wenn Sie die Überschriften für etwas Nützliches verwenden könnten? Hier kommt die zip()-Funktion ins Spiel. Versuchen Sie zuerst, die Dateiüberschriften mit einer Datenzeile zu verknüpfen:

>>> row = next(rows)
>>> row
['AA', '100', '32.20']
>>> list(zip(headers, row))
[ ('name', 'AA'), ('shares', '100'), ('price', '32.20') ]
>>>

Bemerken Sie, wie zip() die Spaltenüberschriften mit den Spaltenwerten verknüpft. Wir haben hier list() verwendet, um das Ergebnis in eine Liste zu verwandeln, damit Sie es sehen können. Normalerweise erstellt zip() einen Iterator, der von einer for-Schleife verarbeitet werden muss.

Diese Verknüpfung ist ein Zwischenschritt bei der Erstellung eines Wörterbuchs. Versuchen Sie jetzt das Folgende:

>>> record = dict(zip(headers, row))
>>> record
{'price': '32.20', 'name': 'AA','shares': '100'}
>>>

Diese Transformation ist eine der nützlichsten Tricks, die Sie kennen sollten, wenn Sie mit vielen Datenfiles arbeiten. Beispielsweise nehmen Sie an, Sie möchten das pcost.py-Programm so ändern, dass es mit verschiedenen Eingabedateien funktioniert, ohne auf die tatsächliche Spaltennummer zu achten, in der der Name, die Anzahl der Anteile und der Preis erscheinen.

Ändern Sie die portfolio_cost()-Funktion in pcost.py so, dass sie wie folgt aussieht:

## pcost.py

def portfolio_cost(filename):
 ...
        for rowno, row in enumerate(rows, start=1):
            record = dict(zip(headers, row))
            try:
                nshares = int(record['shares'])
                price = float(record['price'])
                total_cost += nshares * price
            ## Dies fängt Fehler bei der Umwandlung in int() und float() oben ab
            except ValueError:
                print(f'Zeile {rowno}: Schlechte Zeile: {row}')
 ...

Versuchen Sie jetzt Ihre Funktion auf eine völlig andere Datenfile portfoliodate.csv, die wie folgt aussieht:

name,date,time,shares,price
"AA","6/11/2007","9:50am",100,32.20
"IBM","5/13/2007","4:20pm",50,91.10
"CAT","9/23/2006","1:30pm",150,83.44
"MSFT","5/17/2007","10:30am",200,51.23
"GE","2/1/2006","10:45am",95,40.37
"MSFT","10/31/2006","12:05pm",50,65.10
"IBM","7/9/2006","3:15pm",100,70.44
>>> portfolio_cost('/home/labex/project/portfoliodate.csv')
44671.15
>>>

Wenn Sie es richtig gemacht haben, werden Sie feststellen, dass Ihr Programm weiterhin funktioniert, auch wenn die Datenfile ein völlig anderer Spaltenformat als zuvor hat. Das ist cool!

Die hier vorgenommene Änderung ist subtil, aber signifikant. Anstatt dass portfolio_cost() hartcodiert ist, um eine einzelne feste Dateiformat zu lesen, liest die neue Version jede CSV-Datei und extrahiert die interessanten Werte daraus. Solange die Datei die erforderlichen Spalten hat, wird der Code funktionieren.

Ändern Sie das report.py-Programm, das Sie in Abschnitt 2.3 geschrieben haben, so, dass es die gleiche Technik verwendet, um die Spaltenüberschriften auszuwählen.

Versuchen Sie, das report.py-Programm auf der portfoliodate.csv-Datei auszuführen und sehen Sie, dass es die gleiche Antwort wie zuvor liefert.

Übung 2.17: Umkehren eines Wörterbuchs

Ein Wörterbuch bildet Schlüssel auf Werte ab. Beispielsweise ein Wörterbuch mit Aktienpreisen.

>>> prices = {
        'GOOG' : 490.1,
        'AA' : 23.45,
        'IBM' : 91.1,
        'MSFT' : 34.23
    }
>>>

Wenn Sie die items()-Methode verwenden, können Sie (Schlüssel,Wert)-Paare erhalten:

>>> prices.items()
dict_items([('GOOG', 490.1), ('AA', 23.45), ('IBM', 91.1), ('MSFT', 34.23)])
>>>

Was passiert aber, wenn Sie stattdessen eine Liste von (Wert,Schlüssel)-Paaren erhalten möchten? Hinweis: Verwenden Sie zip().

>>> pricelist = list(zip(prices.values(),prices.keys()))
>>> pricelist
[(490.1, 'GOOG'), (23.45, 'AA'), (91.1, 'IBM'), (34.23, 'MSFT')]
>>>

Warum würden Sie das tun? Einerseits ermöglicht es Ihnen, bestimmte Arten von Datenverarbeitung auf den Wörterbuchdaten durchzuführen.

>>> min(pricelist)
(23.45, 'AA')
>>> max(pricelist)
(490.1, 'GOOG')
>>> sorted(pricelist)
[(23.45, 'AA'), (34.23, 'MSFT'), (91.1, 'IBM'), (490.1, 'GOOG')]
>>>

Dies veranschaulicht auch eine wichtige Eigenschaft von Tupeln. Wenn sie in Vergleichen verwendet werden, werden Tupel elementweise beginnend mit dem ersten Element verglichen. Ähnlich wie Strings zeichenweise verglichen werden.

zip() wird oft in Situationen wie dieser verwendet, in denen Sie Daten aus verschiedenen Quellen paarweise zusammenbringen müssen. Beispielsweise die Spaltennamen mit den Spaltenwerten zu einem Wörterbuch von benanntem Werten zusammenbringen.

Beachten Sie, dass zip() nicht auf Paare beschränkt ist. Beispielsweise können Sie es mit beliebig vielen Eingabelisten verwenden:

>>> a = [1, 2, 3, 4]
>>> b = ['w', 'x', 'y', 'z']
>>> c = [0.2, 0.4, 0.6, 0.8]
>>> list(zip(a, b, c))
[(1, 'w', 0.2), (2, 'x', 0.4), (3, 'y', 0.6), (4, 'z', 0.8))]
>>>

Außerdem sollten Sie wissen, dass zip() sofort stoppt, wenn die kürzeste Eingabesequenz erschöpft ist.

>>> a = [1, 2, 3, 4, 5, 6]
>>> b = ['x', 'y', 'z']
>>> list(zip(a,b))
[(1, 'x'), (2, 'y'), (3, 'z')]
>>>

Zusammenfassung

Herzlichen Glückwunsch! Sie haben das Sequenzen-Labor abgeschlossen. Sie können in LabEx weitere Labs ausprobieren, um Ihre Fähigkeiten zu verbessern.