basics michael poeltl © 2013,2014

Operationen für Sequenzen

In Was sind Sequenzen? hielten wir fest, dass jedes Element (Objekt) einer Sequenz an einer bestimmten Position (sein Index beginnend bei 0) *gefunden* werden kann. Im Unterschied zu dict()-Objekten bleibt die Reihenfolge des Eingangs eines Objekts erhalten, und zum Unterschied eines Mengen-Objekts können member-Objekte einer Sequenz öfter als ein Mal vorkommen.

Wie kann man schnell Informationen aus einem Sequenz-Objekt herausholen? Wie kann man diese Objekte verändern, beeinflussen? Diesen und ähnlichen Fragestellungen wollen wir hier auf den Grund gehen.

Gemeinsame Operationen, Methoden und Funktionen

Alle Sequenzen haben gemeinsame Operatoren/Operationen. Die will ich hier einmal herausheben.

len()

Beginnen tu ich gern mit der len()_-builtin-functin. JEDE Sequenz hat eine bestimmte Länge.

>>> vorname = 'michelle'
>>> len(vorname)
8
>>> t = (18, 'm', (19,20,), 'h',)
>>> len(t)
4
>>> l = []
>>> len(l)
0
>>> amahan = b'michael'
>>> len(amahan)
7
>>> amafrau = bytearray(b'marialuordes')
>>> len(amafrau)
12
>>>
max() und min()

jedes iterierbare Objekt (ergo jede Sequenz) hat ein Minimum (min()) und ein maximum (max()). Beides sind builtin-functions und sehr leicht anwendbar.

>>> t = (18, 0, -3,1.1, 29,)
>>> print("Maximum von tuple ist {}".format( max(t) ) )
Maximum von tuple ist 29
>>> print("Minimum von tuple ist {}".format( min(t) ) )
Minimum von tuple ist -3
>>>

Wirklich einfach. Nun wollen wir die max()-function ausreizen, und auch die Möglichkeit ausschöpfen, mittels eines keys das Kriterium festzulegen, die zur Bestimmung des max/min führen soll.

>>> t = ('michael', 'andreas','richard',)
>>> print( max(t) )
richard
>>> print( max(t, key=lambda s: s[2]) )
andreas
>>> print( min(t) )
andreas
>>> print( min(t, key=lambda s: s[2]) )
michael
>>>

das mit key= als optionalen zweiten Parameter sieht man dann und wann; mit Sicherheit werden wir wieder draufstoßen, wenn wir sorted() bzw. sort() abhandeln werden.

max() und min() funktionieren freilich auch bei den in python3 neu eingeführten Objekttype bytestring und bytearray.

>>> listerl = [ 1,1,2,3,5,8,13,21]
>>> bal = bytearray(listerl)
>>> min(bal)
1
>>> max(bal)
21
>>> print( bal )
bytearray(b'\x01\x01\x02\x03\x05\x08\r\x15')
>>> max(bal)
21
>>> inbyte = bytes('michael',encoding='utf-8')
>>> max(inbyte)
109
>>>

Weißt du noch, wie du herausfinden kannst, welches Zeichen mit 109 gemeint sein kann?

index()

sucht für gewöhnlich die Sequenz nach einem ewissen objekt von links nach rechts ab, und sobald es auf das objekt trifft, gibt es dessen Indexposition aus und beendet sich.

>>> s = 'michael'
>>> s.index('h')
3
>>> s.index('x')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: substring not found
>>>

Wir bekommen eine ValueError-exception, wenn das objekt nicht in der Kollektion ist, auch nicht die feine englische Art.

count()

ist eine Methode, die AN einem Objekt angewandt wird.

>>> vorname.count('a')
0
>>> t.count(8)
0
>>> l.count(' ')
0
>>> amahan.count(b'a')
1
>>> amafrau.count(b'a')
2
>>>
Zugriff

Über die index-Position eines Elements einer Sequenz kann man verlässlich bzw. gezielt darauf zugreifen.
Angenommen wir haben drei Objekte: 'michael', (1,2,3), ['aa', 2, 'bb']

>>> ein_name = 'michael'
>>> ein_tuple = ( 1,2,3, )
>>> eine_liste = [ 'aa', 2, 'bb', ]
>>>

Wie erreiche ich die 2 in ein_tuple?
Genau so

>>> print( ein_tuple[1])
2
>>>

Warum erreichte ich die Zahl 2 (Element des tuples) über Position 1? Ganz einfach, weil bei den Positionsnummern immer mit Null (0) begonnen wird zu zählen.
Das 'h' ist auf Position 3, ergo

>>> print( ein_name[3] )
h
>>>

und 'aa' in eine_liste erreichen wir ueber Position 0 (Null)

>>> print( eine_liste[0] )
aa

Beim slicing werden wir dieses Wissen wieder brauchen.

Konkatenation

Man kann Sequenzen gleichen Typs (str mit str, tuple mit tuple, etc) *zusammenschweißen*.

>>> l1,l2 = [1,2,3],['a','b','c']
>>> l = l1 + l2
>>> print( l )
[1, 2, 3, 'a', 'b', 'c']
>>> n1 = 'michael '
>>> n2 = 'poeltl'
>>> n = n1 + n2
>>> print( n )
michael poeltl
>>> t1,t2 = (1,2,3,), ('a','b','c',)
>>> print( t1 + t2 )
(1, 2, 3, 'a', 'b', 'c')
>>>

Sequenzen vervielfältigen

Man kann Sequenzen leicht Vervielfältigen!

>>> print( n1 * 3)
michael michael michael 
>>> print( n1 * 0)

>>> for i in range(4):
...     print( (3-i)*' ',(2*i+1)*'*' )
... 
    *
   ***
  *****
 *******
>>> listerl = [ 'ni' ]
>>> ergebnis = listerl * 7
>>> print( ergebnis )
['ni', 'ni', 'ni', 'ni', 'ni', 'ni', 'ni']
>>>

Ginge das auch bei anderen Typen, wie einem mapping-Type?

>>> {'eins':1}*5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for *: 'dict' and 'int'
>>>

Wir sehen schon; das flog uns gehörig um die Ohren.

in, not in

Mit Hilfe von in bzw. not in kann man fragen, ob ein gewisser Identifier zu einem Objekt in einer Sequenz vorhanden ist, und man bekommt True oder False (Ja bzw. Nein) als Antwort zurück.
Diese Art zu fragen fand ich von Anfang an sehr pythonic (obwohl man glauben könnt, dass da nix Großartiges dabei ist).

>>> name = 'michael'
>>> 'l' in name
True
>>> 'x' in name
False
>>> l = [1,2,3]
>>> 4 in l
False
>>> bl = bytearray(l)
>>> 3 in bl
True
>>>
enumerate()
>>> help( enumerate )
Help on class enumerate in module builtins:

class enumerate(object)
 |  enumerate(iterable[, start]) -> iterator for index, value of iterable
 |  
 |  Return an enumerate object.  iterable must be another object that supports
 |  iteration.  The enumerate object yields pairs containing a count (from
 |  start, which defaults to zero) and a value yielded by the iterable argument.
 |  enumerate is useful for obtaining an indexed list:
 |      (0, seq[0]), (1, seq[1]), (2, seq[2]), ...
 |  
 ...
>>> 

Die letzte Zeile (0, seq[0]), (1, seq[1]), (2, seq[2]), ...
sagt uns gleich, worum es bei enumerate() geht. Es werden tuple-Paare zurückgegeben (<index>, und SEQUENZ[<index>]). Schau' ma' 'mal...

>>> l2
[1, 2, 3, 4, 5]
>>> l_enumerated = enumerate(l2)
>>> print( type(l_enumerated) )
<class 'enumerate'>
>>> next(l_enumerated)
(0, 1)
>>> next(l_enumerated)
(1, 2)
>>>

Sorry - ich greife jetzt schon einem Thema vor, nämlich jenem von iterator und der next()-function - aber hey - ist nicht schlimm, oder?
Wird noch genauer unter die Lupe genommen werden in basics++.

Schauen wir uns jetzt noch an, wie das geht mit eine gewisse Startzahl setzen, von wo aus die folgenden mit 1 inkrementiert werden, dann werden wir durchiterieren, und andere Objekte ausprobieren, die wir zu den Sequenzen zählen, um zu überprüfen, ob das da verlässlich auch so ist. Schließlich wollen wir auf Nummer sicher gehen.

>>> for index,wert in enumerate(l1):
...     print( str(index+1)+'. ', ' '+str(wert), sep='-->' )
... 
1. --> 1
2. --> 2
3. --> 3
4. --> 4
5. --> 5
>>> l1
[1, 2, 3, 4, 5]
>>> bl = bytearray(l1)
>>> for i in enumerate(bl):
...     print( i )
... 
(0, 1)
(1, 2)
(2, 3)
(3, 4)
(4, 5)
>>>

Mehr Beweise bedarf es wohl nicht, um zu *glauben*, dass das auch smoothly mit bytearrays oder bytestrings geht. Der zweite, optionale Parameter von enumerate gibt an, von wo weg gezählt werden soll. Default ist 0 (Null), aber man könnte es ja auch von 1weg zählen lassen, dann brauch ich auch nicht zum index 1 dazuzählen - also nochmal unser Beispiel von oben:

>>> for index,wert in enumerate( l1, 1 ):
...     print( str(index)+'. ', ' '+str(wert), sep='-->' )
... 
1. --> 1
2. --> 2
3. --> 3
4. --> 4
5. --> 5
>>>
slicing

bedeutet nichts anderes, als in kleinre Stücke teilen (schneiden) bzw. EIN kleineres Stück aus der Sequenz herausschneiden. Beim slicing entsteht in jedem Fall ein neues Objekt (neue ID, neuer Wert). Nochmal langsam, zum Mitschreiben:

Eine Liste lässt sich in place verändern - das bedeutet, dass die Veränderung genau an jenem Speicherort im RAM stattfindet, wo das Objekt liegt. Eine Liste ist muteable (veränderbar), wie wir ja schon desöfteren gehört haben..
Aber, beim Slicing wird ja etwas ausgeschnitten, und nicht die Liste verändert. Deshalb ist in diesem Fall das entstehende Objekt ein neues Listenobjekt (Überraschung!). Das entstehende Objekt hat somit eine neue object-id, und (zumeist) einen neuen Wert.

str-Objekte, bytestrings und tuple sind immuteable, was bedeutet, dass das resultierende (neue) Objekt in jedem Fall ein neues Objekt im Arbeitsspeicher sein wird (eigene, neue object-id).

Lassen wir wieder ein paar Beispiele folgen, denn die zeigen mehr, als Tausend Worte beschreiben könnten.

>>> vorname = 'michael'
>>> print( vorname[0] )
m
>>> print( vorname[-1] )
l
>>> print (vorname[0:2])
mi
>>> print( vorname[:2] )
mi
>>> l1 = [1,2,3,4,5]
>>> l2 = l1[:3]+l1[3:]
>>> l1 is l2
False
>>> id(l1)
139746923055728
>>> id(l2)
139746923086408
>>> l1
[1, 2, 3, 4, 5]
>>> l2
[1, 2, 3, 4, 5]
>>>

Zuerst haben wir uns den Inhalt der ersten Kluppe auf unserer imaginären Wäscheleine uns ausgeben lassen, was im Grunde auch schon slicing ist - man schneidet genau ein Zeichen wie eine Scheibe Brot ab.
Um die ersten zwei Zeichen zu bekommen, musste man bereits den Bereich 0:2 angeben; wobei man die 0 nicht unbedingt schreiben muss. Wie man sieht, reichte auch :2 aus. Beim Listenbeispiel haben wir Liste l1 in zwei Teile zerschnippelt ([:3] -> erstes, zweites, drittes Objekt, [3:] -> viertes Objekt (index-Position 3) bis zum Letzen) und dann concatinated und l2 auf das neue Objekt zeigen lassen. l2 zeigt auf eine Liste, die gleich aussieht wie l1, aber dennoch in einem eigenen Bereich im Arbeitsspeicher zeigt.
Aber zurück zum vorname-Beispiel: wir erleben enumerate zum ersten Mal im Einsatz und slicen uns im Anschluss das zweite bis zum fünften Zeichen. Heißt das, dass das fünfte Zeichen dabei ist? Nein. Bis zum fünften heißt, vor dem fünften Halt zu machen.

>>> for i,j in enumerate(vorname):
...     print( str(i) + '\t' + j )
... 
0       m
1       i
2       c
3       h
4       a
5       e
6       l
>>> print( vorname[2:5] )
cha
>>>

Die Ausgabe mit enumerate() diente uns nur zur Kontrolle. (enumerate bitte merken!)
Wir sehen, dass einfaches slicing einfach ist. Das funktioniert genau gleich wie bei tuple und bei list-Objekten.

>>> vorname[::-2]
'lacm'
>>> vorname[::2]
'mcal'
>>> vorname[::3]
'mhl'
>>> vorname[1::3]
'ia'
>>>

Hier hatten wir jeden zweiten in umgekehrter Reihenfolge (::-2), dann in richtiger Reihenfolge jedes zweite Zeichen und jedes dritte Zeichen ab dem zweiten Zeichen.
Wir merken uns auch für umgekehrte Reihenfolge (::-1).
Und jetzt wollen wir einen slice umdrehen (z.B.: 4:1 umgedreht ist 4:1:-1

>>> name = 'michael poeltl'
>>> name[4:1:-1]
'ahc'
>>> name[5:1:-1]
'eahc'
>>> name[5:2:-1]
'eah'
>>>

und noch was mit list- und tuple-Objekten.

>>> L = [0,1,2,3,4,5,6,7,8]
>>> print( L[2:6] )
[2, 3, 4, 5]
>>> print( L[2:6][::-1] )
[5, 4, 3, 2]
>>> L[6:2:-1] #incl. index6, excl. index2
[6, 5, 4, 3]
>>> L[5:1:-1] #incl. index5 excl. index1
[5, 4, 3, 2]
>>> viertes_tuple = 1,2,3,'aber', 'he', 100,
>>> print( viertes_tuple[1] )
2
>>> print( viertes_tuple[1:] )
(2, 3, 'aber', 'he', 100)
>>> print( viertes_tuple[1:3] )
(2, 3)
>>> print( viertes_tuple[:3] )
(1, 2, 3)
>>> print( viertes_tuple[1:5:2] )
(2, 'aber')
>>>
slice()

Dieses exzessive slicing war ganz schön intensiv. Diese Schreibweise, wie wir sie oben kennengelernt haben, mit *hardcoded* index-Werten verschlechtert die Lesbarkeit deines codes (zumindest wird es manchmal so kolportiert). Damit meine ich, dass
zahlen[ jede_zweite ] gleich nachvollziehbarer wäre, als
zahlen[::2]

Aber wie könnte man das anstellen?
Die Überschrift und der einleitende Text verraten bereits, dass es selbst dafür eine *Lösung* gibt, nämlich die built-in-function slice().

>>> help( slice )
class slice(object)
 |  slice(stop)
 |  slice(start, stop[, step])

Es braucht also ein start und ein stop-Argument, und optional ein step-Argument. None wird eingesetzt, wenn man, zum Beispiel, kein start-Argument setzen mag. Und das sieht dann so aus:

>>> vorname = 'michael'
>>> #start == -1, stop == None
... 
>>> letztes = slice( -1,None )
>>> vorname[ letztes ]
'l'
>>> vorname[::2]
'mcal'
>>> jedes_zweite = slice(None,None,2)
>>> vorname[ jedes_zweite ]
'mcal'
>>> vorname[::-1]
'leahcim'
>>> umgedreht = slice( None,None,-1 )
>>> vorname[ umgedreht ]
'leahcim'
>>> vorname[:-1]
'michae'
>>> a = slice(-1)
>>> vorname[a]
'michae'
>>>

Aha - beim letzten Beispiel haben wir gesehen, dass slice() manchmal das None selber halbwegs intelligent einsetzt.
Um das letzte Zeichen zu bekommen ( [-1] ) braucht man slice(None,-1),
um aber alle Zeichen außer dem Letzten zu bekommen ( [:-1] ) reichte bereits ein slice(-1).
Es ist also schon gewöhnungsbedürftig, aber sicherlich sehr praktisch - zum Beispiel:

>>> zahlen = list( range( 30 ))
>>> jedes_zweite = slice( None,None,2 )
>>> ein_t = tuple('donaudampfschiff')
>>> vorname[ jedes_zweite ]
'mcal'
>>> zahlen[ jedes_zweite ]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28]
>>> ein_t[ jedes_zweite ]
('d', 'n', 'u', 'a', 'p', 's', 'h', 'f')
>>>

Der Name jedes_zweite sagt klar, dass jedes zweite item (beginnend bei index 0 (Null)) genommen werden soll.
Das slice-object kann dann tatsächlich dort und da eingesetzt werden.
Man kann auch ein slice-object dazu verwenden, um items einer Liste hinzuzufügen, oder aus einer Liste zu entfernen.

>>> index_2bis5 = slice(2,5)
>>> zahlen[2:5]
[2, 3, 4]
>>> zahlen[ index_2bis5 ]
[2, 3, 4]
>>> zahlen[ index_2bis5 ] = [ 22, 33, 44 ]
>>> zahlen
[0, 1, 22, 33, 44, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29]
>>> del zahlen[ index_2bis5 ]
>>> zahlen
[0, 1, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29]
>>>

Um mehr infos über das slice-Objekt zu bekommen, kann man die Attribute abfragen, zum Beispiel:

>>> print( index_2bis5.start )
2
>>> print( index_2bis5.stop )
5
>>> print( index_2bis5.step )
None
>>> print( a.start )
None
>>> print( a.stop )
-1
>>>

Es gibt auch noch die indices()-Methode, die helfen soll, IndexError-Exceptions zu vermeiden. Am besten, ich zeig einmal den Hilfetext

>>> help( slice.indices )
Help on method_descriptor:

indices(...)
    S.indices(len) -> (start, stop, stride)

    Assuming a sequence of length len, calculate the start and stop
    indices, and the stride length of the extended slice described by
    S. Out of bounds indices are clipped in a manner consistent with the
    handling of normal slices.
END
>>> vorname[7:10]
vorname[7:10]
''
>>> vorname[10]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: string index out of range
>>> a = slice(10)
>>> a.indices( len(vorname ))
(0, 7, 1)
>>>
reversed()

dreht die Reihenfolge einer Sequenz um, stellt sie quasi auf den Kopf, und es gibt ein generator-Objekt zurück.

>>> t = (1,2,3,4,)
>>> bs = bytes(s,'utf-8')
>>> bal = bytearray(l)
>>> rs = reversed(s)
>>> print( type(rs) )
<class 'reversed'>
>>> for i in rs:
...     print(i, end=' ')
... 
l e a h c i m >>>
>>> rl = reversed(l)
>>> next(rl)
4
>>> next(rl)
3
>>> next(rl)
2
>>> next(rl)
1
>>> next(rl)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> rbs = reversed(bs)
>>> chr( next(rbs) )
'l'
>>>

ich weiss schon - wir haben generator-Objekte noch nicht kennengelernt - kommen dran in basics++.

sorted()

ist eine built-in-function, welche die Objekte in einer Sequenz sortiert. Das resultierende (nach einem gewissen Kriterium sortiert) list-Objekt wird an die aufrufende Stelle zurückgegeben.

Oft will man alphabetisch osrtieren, oder mit dem kleinsten beginnend - man kann aber auch andere Sortier-kriterien wünschen, wie zum Beispiel

>>> t = (18,5,2,13)
>>> tsorted = sorted(T)
>>> print( tsorted )
[2, 5, 13, 18]
>>>

die zu sortierenden Objekte müssen aber vom selben Typ sein, sonst krachts im Gebälk

>>> tuplelist = [ (9,2), (10,11,155,2),(7,), (2,3,4,5,6,76)]
>>> sorted(tuplelist)
[(2, 3, 4, 5, 6, 76), (7,), (9, 2), (10, 11, 155, 2)]
>>> sorted(tuplelist, key=len)
[(7,), (9, 2), (10, 11, 155, 2), (2, 3, 4, 5, 6, 76)]
>>> sorted(tuplelist, key=sum)
[(7,), (9, 2), (2, 3, 4, 5, 6, 76), (10, 11, 155, 2)]
>>>

Wie du siehst, konnten wir mit dem Parameter key= der sorted-Funktion das Sortier-Kriterium vorgeben. Die items der Liste werden der angeführten Funktion übergeben und danach sortiert.
Da kann man sich auch eine eigene Funktion schreiben und key auf diese zeigen lassen.

>>> sorted(tuplelist, key=(lambda x: sum(x)%8))
[(2, 3, 4, 5, 6, 76), (10, 11, 155, 2), (9, 2), (7,)]
>>>

Die Summe von (2, 3, 4, 5, 6, 76) ergab 96. 96 dividiert durch 8 ergibt 12Mal mit 0Rest - und der Rest interessierte uns - weil der 0 ist, wurde (2, 3, 4, 5, 6, 76) nach ganz vorne gereiht. Die anderen tuple kannst ja selber nachrechnen.

Arbeiten mit string-Objekten

Was kann man sonst noch alles mit Zeichenketten anstellen?
Ärmel hochkrempeln und Los gehts ;-)

isalpha()

ist eine Methode an einem str-Objekt. Die Antwort auf die Frage: Bestehen alle *Teile* des str-Objekts aus Zeichen des Alphabets? lautet True (Ja) oder False (Nein).
Wenn das str-Objekt leer ist, dann wird mit False geantwortet.

>>> vorname = 'michael'
>>> vorname.isalpha()
True
>>> mischmasch = 'm1i2c3h4'
>>> mischmasch.isalpha()
False
>>> leer = ''
>>> leer.isalpha()
False
>>> leer2 = str()
>>>

upper(), lower()

Alle Zeichen eines strings in Großbuchstaben bzw. in Kleinbuchstaben umzustricken; dazu bedarf es die upper() bzw. die lower90 Methode an einem str-Objekt.

>>> strudel = 'aBer eIn ApfelSTrudel'
>>> print( strudel.lower() )
aber ein apfelstrudel
>>> print( strudel.upper() )
ABER EIN APFELSTRUDEL
>>>

islower(), isupper()

islower() und isupper() sind beides Methoden an einem str-Objekt. Der Interpreter gibt True zurück, wenn man mit islower()? nachfragt, ob alle Zeichen eines str-Objekts aus Kleinbuchstaben bestehen, andernfalls False. Gleiches gilt fuer isupper().
Ein Beispiel sagt wieder mehr als Tausend Worte.

>>> klein = 'michael'
>>> kleinmix = 'michael123'
>>> gross = 'MICHAEL'
>>> grossmix = 'MICHAEL123'
>>> mix = 'MiChAeL'
>>> klein.islower()
True
>>> kleinmix.islower()
True
>>> klein.isupper()
False
>>> gross.isupper()
True
>>> grossmix.isupper()
True
>>> mix.islower()
False
>>> mix.isupper()
False
>>>

Bemerkenswert ist, dass bei alphanumerische Objekte (str-Objekte die aus Buchstaben und Zahlen bestehen), wie jene, auf die die identifier grossmix/kleinmix zeigen, die Zahlen (eigentlich klarerweise) unberücksichtigt bleiben!

Ein Beispiel aus der mathematik. Thema Stellenwertangabe, also
Einerstelle (E), Zehnerstelle (Z), Hunderterstelle (H), aber auch Zehntelstelle (z), Hundertstelstelle (h), etc. - 'H' und 'h' haben in diesem Kontext also unterschiedliche Bedeutungen, und da befragt man also das Zeichen, ob es ein großes oder kleines H ist.
(falls du bessere Vergleiche/Beispiele hast, dann ergänze ich diesen part gerne! Just send email.

isdigit()

Ist ein Element eines str-Objekts eine Zahl?

>>> '5'.isdigit()
True
>>> 'a'.isdigit()
False
>>> for i in grossmix:
...     if i.isdigit():
...         print( i )
... 
1
2
3
>>>

isalnum()

alnum ist die Abkürzung von alphanumeric und meint, dass im str-Objekt eine Mischung von Zahlen mit Buchstaben vorkommt, wobei ein str-Objekt bestehend aus Buchstaben auch schon als Mischung von Buchstaben und zero Zahlen verstanden wird, oder auch als Mischung von zero Buchstaben und Zahlen. Die Antwort auf die Fragestellung lautet wieder True/False.

>>> kette = 'abcd'
>>> zahlen = '123'
>>> mischmasch = 'a1b2c3'
>>> leer = str()
>>> kette.isalnum()
True
>>> zahlen.isalnum()
True
>>> mischmasch.isalnum()
True
>>> leer.isalnum()
False
>>>

zahlen.isdigit()
hätte auch ein True zurückgegeben. Was, wenn im str-Objekt andere Zeichen außer Zahlen und Buchstaben vorkommen? Zum Beispiel ein Punkt?

>>> anderes = 'a.2'
>>> anderes.isalnum()
False
>>> leerzeichen = 'a b c'
>>> leerzeichen.isalnum()
False
>>> gleitpunkt = '3.1415'
>>> gleitpunkt.isalnum()
False
>>>

Genau - das sind KEINE alphanumerischen Zeichenketten!

isspace()

Unter whitespace meint man Leerzeichen, Tab-Hops und newline. Bilden solche Zeichen ein str-Objekt, so kann man das Abfragen, ob dem so sei mit der isspace()-Methode. leerzeichen wurde vom Beispiel der vorigen section (isalnum) übernommen.

>>> leerzeichen.isspace()
False
>>> whitespace = ' \t\n'
>>> whitespace.isspace()
True
>>> leer
''
>>> leer.isspace()
False
>>>

startswith(), endswith()

Beginnt string mit character 'x' oder mit 'mic'? (True/False)
Endet string mit 'a' oder mit 'ant'? (True/False)

>>> vorname = 'nico'
>>> vorname.startswith('m')
False
>>> print( vorname.startswith( ('n', 'o') ) )
True
>>> print( vorname.startswith( ('m','o') ) )
False
>>> print( vorname.endswith( ('m', 'ni', 'Co', 'ico') ) )
True
>>>

startswith() und endswith() können mehrere substrings in ein tuple-Objekt verpackt zum Ausprobieren als EIN Parameter übergeben werden.

>>> print( vorname.startswith('ni') )
True
>>>

capitalize()

>>> print (vorname.capitalize())
Nico
>>>

title()

>>> worte = "haus antenne noch eins"
>>> print (worte.capitalize())
Haus antenne noch eins
>>> print (worte.title())
Haus Antenne Noch Eins
>>>

Jedes wort beginnt nun mit einem Grossbuchstaben. Aber nicht nur das - egal, welche Buchstaben gross oder klein geschrieben waren, nach der methode an dem str-Objekt gitl, (z.B. bei title()), erster Buchstabe jedes wortes gross, Rest kleingeschrieben

>>> grossgeschrieben = 'ich bestehe aus lauter grossbuchstaben'.upper()
>>> print (grossgeschrieben)
ICH BESTEHE AUS LAUTER GROSSBUCHSTABEN
>>> print (grossgeschrieben.capitalize())
Ich bestehe aus lauter grossbuchstaben
>>> print (grossgeschrieben.title())
Ich Bestehe Aus Lauter Grossbuchstaben
>>>

split()

zersaebelt strings - per default ist whitespace das Trennzeichen

>>> zersaebel = 'ich   moechte\nin\teine  Liste'
>>> print (zersaebel)
ich   moechte
in      eine  Liste
>>> print (zersaebel.split())
['ich', 'moechte', 'in', 'eine', 'Liste']
>>>
der string wird zerschnitten und als Liste zurueckgegeben das 'Trennzeichen' scheint nicht mehr auf
>>> pfad = '/da:/dort/:/hier'
>>> print (pfad.split(':'))
['/da', '/dort/', '/hier']
>>> print ('mississippi'.split('ss'))
['mi', 'i', 'ippi']
>>> print ('mississippi'.split('ss',1))
['mi', 'issippi']
>>>
mit dem zweiten Argumet konnte ich festlegen, wie oft der string durch gewuenschtes Trennzeichen getrennt werden soll!
>>> print (zaehle.split(' ',3))
['1', '2', '3', '4 5']
>>>
>>> 'eins zwei drei vier'.split()
['eins', 'zwei', 'drei', 'vier']
>>>

partition()

>>> 'eins zwei drei vier'.partition(' ')
('eins', ' ', 'zwei drei vier')
>>>

escape-Sequenzen

Könnte durchaus sein, dass ich die escape-Sequenzen schon an anderer Stelle gezeigt habe (sollt ich nochmal überprüfen).
Escape heißt's deswegen, weil die Angaben mit einem Backslash eingeleitet werden.
Zum Beispiel, um einen Tabulator zu setzen, tippte man
Backslash t
also \t.
Weiters haben wir zur Auswahl

\'      single quote
\"      double quote
\\      dreimal darfst raten
\a      bell
\b      backspace
\f      formfeed
\n      newline
\r      carriage return
\t      tab
\v      vertical tab

Es gibt da noch octal, hexadecimal und unicode escape sequences.
Zeichen in der ASCII-Tabellle, z.B., können mittels octal oder hexadecimal dargestellt werden.

>>> print( ord('m') )
109
>>> print( hex(ord('m')) )
0x6d
>>> print( oct(ord('m')) )
0o155
>>> hex(0o155)
'0x6d'
>>> oct(0x6d)
'0o155'
>>> int(0x6d)
109
>>> chr(0x6d)
'm'
>>>

m ist also
==> decimal 109 ==> octal 155 und ==> hexadecimal 6d

>>> print ('m','\155','\x6d')
m m m
>>>

Hier sehen wir also eine octal- und eine hexadecimal-escape-sequence ('\155', '\x6d').
interessanter wirds bei nicht-druckbaren Zeichen, wie z.B. newline \n
\n in octal 012 und in hexa 0a.

>>> print ('\n','\012','\x0a')

 
  

>>>

Man sieht da net viel vor lauter whitespace ;-)
In python3.X sind strings per default unicode-strings --> es lassen sich viele (wenn nicht alle) Zeichen der diversen Sprachen darstellen.

Die unicode-Tabelle ist natürlich viel größer als die ascii-Tabelle (die ja eine kleine Teilmenge der unicode-Tabelle ist).
Hier nur eine kleine Kostprobe - später (nicht im basics Teil) können/werden wir uns näher mit unicode befassen.

>>> #letter a
>>> ord('a')
97
>>> ord('\N{latin small letter a}')
97
>>> unicode_a = '\N{latin small letter a}'
>>> print (unicode_a)
a
>>> print (ord(unicode_a))
97
>>> print (oct(97), hex(97))
0o141 0x61
>>> #french a with acute
>>> print (ord('\N{latin small letter a with acute}'))
225
>>> print (oct(225), hex(225))
0o341 0xe1
>>> print ('\341', '\xe1', '\u00e1')
á á á
>>>

Am Ende war die '\u00<hexa>'-schreibweise;
mit chr() kannst du von der Zahl rückschließen auf das Zeichen.
chr() ist praktisch für die nächste Kostprobe (und dann belassen wir es vorerst dabei - wrd uns da und dort begegnen).

>>> for i in range(240,265):
...     print (i,'\t',chr(i))
... 
240      ð
241      ñ
242      ò
243      ó
244      ô
245      õ
246      ö
247      ÷
248      ø
249      ù
250      ú
251      û
252      ü
253      ý
254      þ
255      ÿ
256      Ā
257      ā
258      Ă
259      ă
260      Ą
261      ą
262      Ć
263      ć
264      Ĉ
>>>

zu int() noch eine Ergänzung!
int() gibt eine Dezimalzahl (Zahl base 10) zurück.

>>> szahl10 = '128'
>>> izahl10 = int(szahl10)
>>> print (izahl10, type(izahl10))
128 <class 'int'>
>>>

Was wenn wir einen string, der eigentlich eine octal-Zahl darstellt, in einen int umwandeln wollten?

>>> szahl8 = oct(1024)
>>> print (szahl8, type(szahl8))
0o2000 <class 'str'>
>>> print (int(szahl8))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '0o2000'
>>>

Die Ergänzung zu int() ist nun der zweite Parameter dieser built-in-function, der die base angibt, als was der string zu sehen ist.

>>> print (int(szahl8, 8))
1024
>>>

und noch ein Beispiel:

>>> print (int('1000',2), int('1000',8), int('1000',10), int('1000',16))
8 512 1000 4096
>>> print (int('ffffff',16))
16777215
>>>

escape-Sequenzen

vor der ersten Methode eine bereits bekannte built-in Funktion - sorted()

>>> vorname = 'nico'
>>> sorted(vorname)
['c', 'i', 'n', 'o']
>>>

sorted() bekam in diesem Fall eine Zeichenkette übergeben und gibt diese sortiert als list-Objekt zurück. Sortiert wird vom Niedrigsten bis zum Höchsten.
Mittels reverse=True kann man die Sortier-Reihenfolge bei sorted() umkehren.

>>> sorted(vorname)[::-1]
['o', 'n', 'i', 'c']
>>>

So haben wir die Reihenfolge umgedreht, indem wir den uns bekannten Trick für's list-Objekt anwandten. ABER, wir wollen doch einen Parameter von der sorted-Funktion nutzen.

>>> sorted(vorname, reverse=True)
['o', 'n', 'i', 'c']
>>>

Arbeiten mit tuple-objekten

Arbeiten mit list-objekten

>>> spiel = ['zero','eins','zwei','drei','vier','fuenf']
>>> print (spiel[2:-2])
['zwei', 'drei']
>>>

Eine weitere Besonderheit bei Listen ist:

>>> seeds = []
>>> seeds += [9, 1, 5, "poppy"]
>>> seeds += 1,2,3,
>>> print (seeds)
[9, 1, 5, 'poppy', 1, 2, 3]
>>>
>>> L2 = [ [0]*5 ]*3
>>> print ( L2 )
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
>>> L3 = [ [0 for col in range(5)] for row in range(3)]
>>> for i in L3:
...     print ( id(i) )
... 
140596943688624
140596943692432
140596927057072
>>> for i in L2:
...     print ( id(i) )
... 
140596943688840
140596943688840
140596943688840
>>>

Das 5x3 array L2 hatte ein Problem, nämlich, dass alle Listen in L2 eigentlich genau aufderselben Adresse im Arbeitsspeicher liegen. Veränderst Du einen Eintrag, sieht man das in allen anderen Listen von L2. Und das ist einmal nicht das, was wir brauchten.
Aber lasst uns einfach ein item in eine der Listen einfuegen, also L23[0][0] und L3[0][0], und checken dann den content mit print().

>>> L2[0][0] = 'hurrra'
>>> L3[0][0] = 'hurra'
>>> print ( L2 )
[['hurrra', 0, 0, 0, 0], ['hurrra', 0, 0, 0, 0], ['hurrra', 0, 0, 0, 0]]
>>> print ( L3 )
[['hurra', 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
>>>

Unter Zuhilfenahme der list-comprehension schaffte ich es, drei eigenständige (jede mit eigener Speicheradresse im memory) Listen zu bauen.

list comprehension ist manchmal wirklich erste Wahl!
Zum Beispiel sah ich ein Beispiel in python cookbook, wo eine Liste mit 4Spalten (col) und 3Zeilen (rows) gegeben war. Jetzt braucht man eine neue Liste, zwar mit derselben Anzahl an Zeilen, aber dafür soll die zweite spalte gelöscht werden und die restlichen in einer anderen Reihenfolge eingebaut werden.
Und GENAU dafür sind list-comprehensions wirklich super.
ich zeigs euch gleich

>>> curlist = [[1,2,3,4], [5,6,7,8],[9,10,11,12]]
>>> newlist = [ [row[pos] for pos in (0,3,2)] for row in curlist]
>>> print (curlist)
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
>>> print (newlist)
[[1, 4, 3], [5, 8, 7], [9, 12, 11]]
>>>

Hier wurde also eine neue Liste ebaut. Wollte man aber die existierende in place anpassen, dann würde man das so angehen:

>>> print (curlist)
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
>>> curlist[:] = [ [row[pos] for pos in (0,3,2)] for row in curlist]
>>> print (curlist)
[[1, 4, 3], [5, 8, 7], [9, 12, 11]]
>>>

Was, wenn man die Zeilen einer Liste aus Listen in Spalten (columns), und Spalten in Zeilen umwandeln muss/möchte?

>>> alt = [ ['a','b','c'], ['d','e','f'], ['g','h','i'] ]
>>> neu = [ [a[col] for a in alt] for col in range(len(alt)) ]
>>> print (neu)
[['a', 'd', 'g'], ['b', 'e', 'h'], ['c', 'f', 'i']]
>>>

Die einzelnen Zeilen (rows) wurden tatsächlich in der resultierenden zweidimensionalen Liste su Spalten!
Faszinierend!

Eine Besonderheit stellt L[:] dar.
Das tut nix anderes, als eine zweite (idente) Liste im Arbeitsspeicher zu erzeugen (eigene object-id).
Und wozu braucht man sowas? Sehen wir uns folgendes Beispiel an:

>>> L = [0,1,2,3,4,5,6,7,8]
>>> for i in L:
...     if i % 2 == 0:
...         L.remove(i)
... 
>>> print (L)
[1, 3, 5, 7]
>>>

Alles bestens! Es passierte genau das, was wir wollten. 0 und die geraden natürlichen Zahlen wurden aus der Liste entfernt.
ABER
Leider ist dies bloßer Zufall gewesen, weil jeder zweite Eintrag eine gerade Zahl war. Schauen wir uns an was passiert, wenn dem nicht so ist.

>>> L = [0,2,3,1,22,14,12,11,9,36,12]
>>> for i in L:
...     if i % 2 == 0:
...         L.remove(i)
... 
>>> print (L)
[2, 3, 1, 14, 11, 9, 12]
>>>

OUPS!
Jetzt sind wir mit dem Ergebnis alles andere als zufrieden, nicht wahr?
Die Erklärung ist die:
Beim Durchiterieren merkt sich die for-Schleife, bei welcher index-Position sie gerade war/ist. Wird etwas aus der Liste entfernt, dann rutscht die index-Position um eine Stelle nach links ab dem Entfernten Objekt.
[0,2,3...]
Gleich zu Beginn trifft die for-Schleife auf 0 - die erfüllt die Kondition (i % 2 == 0) und wird somit aus der Liste entfernt. 2, was vorher auf index-Position 1 lag, rutscht nun auf index-Position 0, 3 rutscht auf 1 und so weiter.
Die for-Schleife kriegt aber davon nichts mit - die weiß nur, dass sie die erste Position (Index 0) fertig hat, und dass nun Index 1 drankommt - das nächste item ist also 3, und nicht 2!!!
Somit bleibt 2 in der Liste, und du wunderst dich, dass das Programm einen Fehler hat.
Die Lösung ist einfach. Wir lassen die for-Schleife durch eine Kopie vn L iterieren, und dann treffen wir ganz sicher alle geraden Zahlen. Die Kopie von L ist L[:].

>>> L = [0,2,3,1,22,14,12,11,9,36,12]
>>> for i in L[:]:
...     if i % 2 == 0:
...         L.remove(i)
... 
>>> print (L)
[3, 1, 11, 9]
>>>

An solchen Kleinigkeiten kann es manchmal scheitern!
Hier noch ien Beispiel:

>>> vorname = list('natanael')
>>> print (vorname)
['n', 'a', 't', 'a', 'n', 'a', 'e', 'l']
>>> for c in vorname:
...     print (c)
...     if c == 'a':
...         vorname.remove(c)
...
n
a
a
a
l
>>> print (vorname)
['n', 't', 'n', 'e', 'l']
>>>

Mit dem, was wir schon wissen, können wir es richtig schreiben.

>>> vorname = list('natanael')
>>> print (vorname)
['n', 'a', 't', 'a', 'n', 'a', 'e', 'l']
>>> for c in vorname[:]:
...     print (c)
...     if c == 'a':
...         vorname.remove(c)
...
n
a
t
a
n
a
e
l
>>> print (vorname)
['n', 't', 'n', 'e', 'l']
>>>

Es wurde die Liste (flach) im Hauptspeicher kopiert, durch die Kopie wird durchiteriert, und an der eigentlichen Liste wird geschnippselt.

Mir kommt gerade die Idee, dass man so feststellenn könnte, ob gerade Zahlen in L niemals Nachbarn sind. Ob man eine solche Funktion jemals brauchen kann, wage ich zu bezweifeln ;-)

>>> def gcheck(L = []):
...     for i in L:
...         if i % 2 == 0:
...             L.remove(i)
...     if L:
...         for i in L[:]:
...             if i % 2 == 0:
...                 return False
...         return True
...     else:
...         return False
... 
>>> L1 = [1,1,2,2,3,3,4,4,5,5,6,6]
>>> L2 = [1,2,3,4,5,6,7]
>>> gcheck(L1)
False
>>> gcheck(L2)
True
>>>

hier nochmal die gemeinsamen Operationen einfach aufgelistet.

len(s)                      gibt die Anzahl der members aus
s.count( <to-count> )       gibt an, wie oft to-count
                           in s vorkommt.
slicing                    xxx

Arbeiten mit bytestring-Objekten

Arbeiten mit list-Objekten

Da ich ja schon in Sequenzen/list schon vieles erwähnt und gezeigt habe, was Methoden (Operationen) an list-Objekten angeht,

Arbeiten mit bytearray-Objekten


Hier geht es zum Seitenanfang und da zurück zur basics-Seite