basics michael poeltl © 2013,2014,2015

sequenzen


Was sind sequenzen?

Ich finde ja das englische Wort für Sequenz schöner -> sequence. Was das ist?
Aus dem Englischen ins Deutsche übersetzt bedeutet sequence Aneinanderreihung, Reihe, Folge, Ab(lauf)folge.
Eine sequence ist eine *Sammlung* von Objekten bzw. zu einer Gruppe zusammengefasster Objekte. Diese Objekte einer sequence nennt man auch gerne items.

Jedes Element solch einer sequence wird an einer bestimmten Position gefunden (offset), die wir mit der Positionsnummer, dem Index, schon beim shell-scripting (arrays) kennengelernt haben.
Die Zählung der Indizes beginnt IMMER bei Null.
Das zweite Element (item) einer bestimmten sequence hat Index-Nummer eins, das dritte Index-Nummer zwei, undsoweiter undsofort.
Wenn Du das letzte item brauchst und es zu lange dauert, bei NULL zu zählen zu beginnen, kann man auch das letzte Element einer sequence mit -1 ansprechen, das Vorletzte mit -2 etc.

Bei Sequenzen hat mir anfangs folgende Vorstellung zu einem besseren Verständnis und leichterem Merken verholfen, und ich hoff, es hilft Dir ebenfalls:
Ich stelle mir eine Wäscheleine als Sequenz-Objekt eines gewissen Typs (str, list, ...) vor, und ich werde im Anschluss diese Vorstellung etwas strapazieren.
Die Wäscheleine ist zunächst noch ein leeres Sequenz-Objekt. Sequenz-Objekt sagt aber schon aus, dass Objekte in einer bestimmten Reihenfolge aufgehängt sein werden, was bedeutet, dass wir Objekte innerhalb eines sequence-Objekts über deren Position finden.
Objekte (T-Shirts, Socken, Luftballons, Glockenspiel, Fotos, etc) werden an dieser Wäscheleine mittels einer Kluppe befestigt und werden so zum Mitglied (member) dieser bestimmten Wäscheleine (ich kann ja mehrere Wäscheleinen haben, die ich unterschiedlich benenne und wo ich festlegen könnte, dass ich auf Wäscheleine A ausschließlich Socken aufhängen darf).
Abfragen wie: blau-gestreifte Socke auf Wäscheleine? (also xyz is member?) werden mit True (wahr) oder False (falsch, unwahr), von dem, der die Frage beantworten soll (dem python-Interpreter) beantwortet.
Die äußerst linke Wäschekluppe ist die erste Kluppe und hat den Index 0, die nächste rechts davon hat Index 1, usw.
Befestige ich nun ein Objekt mittels neuer Kluppe zwischen 0 und 1, dann hat die Kluppe mit dem neuen Objekt Positionsnummer 1, und die Kluppe, die vorher auf Position 1 war, steckt nun auf Position 2. Soll die Socke an Kluppe 1 durch ein T-Shirt ersetzt werden, dann ändert sich an der Anzahl hängender Objekte nichts - es wurde nur Socke aus/von dem Objekt Wäscheleine entfernt und durch das Objekt T-Shirt ersetzt.
Die Nummerierung der Kluppen beginnt immer bei 0. Es KANN KEINE Kluppe mit Index 2 geben, wenn es nicht schon Kluppe an erster und zweiter Position (Index 0 und Index 1) gibt. Der Befehl: Hänge schwarze Socke auf Wäscheleine an Positionsnummer 28 würde mit einer Fehlermeldung beantwortet werden, da erst drei items an der Leine hängen, und somit eine fundamentale sequence-Regel gebrochen wurde.
Die Objekte auf der Wäscheleine werden über die Nummer der Kluppe angesprochen. zum Beispiel: Sag mir, welches Objekt ist an der Kluppe 2 befestigt?
Wir werden das so aufschreiben:
print( waescheleine[2] ) (Kluppe Nr. 2 ist die dritte von links, da wir ja bei 0 zu zählen beginnen!)

Das Wort python, zum Beispiel, könnten wir uns nun so vorstellen, dass die einzelnen Buchstaben an der Wäsceleine hängen. p an der Kluppe mit Index 0, y an der 1, t an der 2 usw.. Die Reihenfolge ist hier also ganz wichtig, damit die Buchstabenkette (Zeichenkette oder string) für uns dann auch einen Sinn ergibt.

Eines ist noch zu sagen. Was, wenn ich etwas ändern möchte? Gemeint ist zur Wäscheleine hin, und die blaue Socke an Kluppe 3 durch eine schwarze socke ersetzen.
Das Objekt Wäscheleine wird also in place geändert, da es sich ja immer noch um dieselbe Leine handelt, wie zuvor. In diesem Fall war das Sequenz-Objekt muteable, was veränderbar bedeutet.
Im Gegensatz dazu gibt es sequence-Objekte, die immuteable (unveränderbar) sind. Ich kann dann das Objekt nicht in place ändern, sondern es wird ein neues Objekt im RAM gebaut (neues Objekt vom selben Typ, aber neue eindeutige Objekt-Id).
Listen sind, zum Beispiel, ein bestimmter Objekt-Typ, der den fundamentalen Regeln eines Sequenz-Objekts folgt, die veränderbar sind, und Tuple ist ein anderer Objekt-Typ, der unveränderbar (immuteable) ist.

Was du dir merken musst ist, dass du über den Index auf ein einzelnes Element einer Sequenz zugreifen kannst.
Beispiele aus dem täglichen Leben gegriffen, die eine Sequenz darstellen sind, zum Beispiel, ein Haus mit mehreren Stockserken, oder das Periodensystem, oder die DNA. Kannst dir ruhig ein paar Gedanken darüber machen! ;-)

In python unterscheidet man mehrere Arten von Sequenzen: Zeichenketten (string), bytestring, bytearray, tuple und list (Listen).

das Objekt str

Zeichenketten sind in python3 per default unicode-strings (UTF-8).
Wie kann man das herausfinden/überprüfen, dass das so ist?

>>> import sys

Und hier ein guter comment zu import von Nicholas McGuire:
<hofrat> imports basically just grab the functions and update the namespace, and if necessary the module will do some initialization if called up on the first time. help('import') for a bit more details.
Note that namespace is basically hierarchical and the prime element of this hirarchy is the package - so after importing 'sys' we get the namespace of sys as i.e. sys.getdefaultencoding() </hofrat>

>>> print( sys.getdefaultencoding() )
utf-8
>>> 

Ähnlich kann man python ausgeben lassen, was für ein encoding (höchstwahrscheinlich) auf der Kommandozeile (bash, zsh, etc.) eingestellt ist.

>>> print( sys.stdout.encoding )
UTF-8
>>>

Man kann also die Sonderzeichen im deutschen Alphabet einsetzen und viele andere anderer Sprachen. Aktuell stehen dir mit unicode ca. einhundertTausend verschiedene druckbare Zeichen zur Verfügung. Siehe dazu auch unicode.org.
Jedem Zeichen sind eine Nummer als vier- oder achtstellige Hexadezimalzahl (16-bit oder 32-bit) und ein Name eindeutig zugeordnet.

In den unicode-charts kann man die Nummer zu einem bestimmten Zeichen finden. Zum Beispiel für den griechischen Buchstaben Pi:

>>> print( '\u03C0' )
π
>>>

Von strings hat man schon gehört, wenn man mit Computern arbeitet. Manche glauben, dass die <str>-Taste am Keyboard die Abkürzung für string ist, was falsch ist, denn das keyboard-str steht für Steuerung.
Was es nun mit diesem sehr wichtigen Objekt-Typ auf sich hat, werden die kommenden Zeilen und Seiten aufzeigen.

Das Kennzeichen von Zeichenketten (eine Kette aus Zeichen) ist, dass sie in Anführungsstrichen eingeschlossen sind.
Es wird NICHT zwischen einfachen und doppelten Anführungszeichen unterschieden.
string mit einfachen Anführungszeichen eingeleitet, muss auch mit einfachen Anführungszeichen beendet werden.
Sonderfall:
Die dreifachen Anführungszeichen (single oder double)
''' x ''' oder """ x """
schließen auch Zeichenketten ein. Diese besondere Art setzt Du dann ein, wenn Dein string ein mehrzeiliger ist, und du nicht explizit Zeilenumbrüche (\n) im string setzen möchtest - denn das triple-quoting merkt sich die \n (Zeilenumbrüche), die sich durch betätigen der <ENTER>-Taste ergeben!
ich brauche das (ausschließlich), wenn ich einen sogenannten docstring verfasse.
docstrings sind man-Texte zu functions, classes, Module.

>>> print( 'h' )
h
>>>  print( "h" )
h
>>> print ( 'h" )
  File "<stdin>", line 1
    print ( 'h" )
                ^
SyntaxError: EOL while scanning string literal
>>>  vorname = 'michael'
>>> print( vorname[3] )
h
>>>  bday = '20.02.1932'
>>> print( bday )
20.02.1932
>>>

Was aber, wenn ein Anführungszeichen bzw. ein single quote als Apostroph im string vorkommt? Kein Problem, solange die sich von den die Zeichenkette einschließenden quotes unterscheiden.

>>> print( "it's" )
it's
>>>  print( 'Hello "Mr. Smith"' )
Hello "Mr. Smith"
>>> 

Ein Zeichen (character) ist eine Zeichenkette mit der Länge 1.

>>> help( len )
Help on built-in function len in module builtins:

len(...)
    len(object) -> integer
    
    Return the number of items of a sequence or mapping.
(END)
>>> anfang = 'M'
>>> len( anfang )
1
>>> name = 'michael'
>>> len( name )
7
>>> 

Mit der built-in function len() konnten wir uns also die Länge der Sequenz ausgeben lassen.

Python bietet uns zwei built-in Funktionen. chr() und ord(). ord() bekommt als Argument ein Zeichen und gibt den int des Zeichens der unicode-Tabelle zurück, und chr() bekommt als Argument den int des Zeichens und gibt das Zeichen zurück. Ich wei7szlig; schon - in der unicode-Tabelle stehen Hexadezimalzahlen; daher muss man die vorher in einen int umwandeln.

>>> chr( 44 )
','
>>> ord( ',' )
44
>>>  print( '\u03C0' )
π
>>> int( '03C0',16 )
960
>>> chr( 960 )
'π'
>>> ord( 'π' )
960
>>> hex( 960 )
'0x3c0'
>>>

Du kannst dir in jedem Fall zu ord() chr() und hex() die Hilfe ( help() ) aufrufen!

Wir haben schon von der membership in der intro zu sequences gelesen. Kommt Objekt in Sequenz xyz vor?
Das keyword in ist da unser kleines Helferlein.

>>> name = 'michael'
>>> 'a' in name
True
>>> 'z' in name
False
>>> 

'a' in name! So einfach geht die Abfrage, und die Antwort ist ja (True) oder nein (False).
Was ist der höchste und der niedrigste character (höchster oder niedrigster unicode-Wert)?

>>> max( name )
'm'
>>> min( name )
'a'
>>> 

Auch das war richtig. m ist lt. unicode-code das höchste Zeichen in diesem Vornamen, und a ist das niedrigste Zeichen.
Auch das wurde also zufriedenstellend erledigt.

Ein leerer string läßt sich auf folgende Arten erzeugen:

>>> a = str()
>>> b = ''
>>> c = ' '
>>> a is c
False
>>> a is b
True
>>> b is c
False
>>> c is not b
True
>>>

Bei der Bildung eines leeren strings (string mit Länge 0) musst Du aufpassen, nicht ein Leerzeichen ([white]space) einzubauen. Du siehst:
a is not c!

input() vom user

ist read vom shell-scripting ähnlich

>>> vorname = input('dein vorname? ')
dein vorname? Nicholas
>>> print( vorname )
Nicholas
>>>

in python3 liefert input() immer ein Objekt vom Typ str zurück!
Im Folgenden verwende ich die builtin-function format(); einfach einmal hinnehmen und genießen; format() wird ohnehin noch ausführlich erklärt.

>>> alter = input( 'dein Alter: ' )
dein alter: 42
>>> anzahl = input( 'wieviele kinokarten? ' )
wieviele kinokarten? 3
>>> print( "{0} Kinokarten kosten {1} Euro".format( anzahl, float( anzahl )*10 ) )
3 Kinokarten kosten 30.0 Euro
>>> print( type( alter ) )
<class 'str'>
>>>

eval

user@host:> cat bsp/basics/rechteck02.py

#  zweites Rechteck
#+ laenge/breite kommen vom user
#+ Einheit: [cm]
#+ Programm ausfuehren:
#+ python3 rechteck02.py

#laenge = 10
laenge = eval( input( "Laenge des Rechtecks? ") )
#breite = 8
breite = eval( input( "Breite des Rechtecks? " ) )
#VORSICHT  -  falsche Eingaben werden noch nicht abgefangen - es kracht also, wen
#uer falsche Eingaben wie buchstaben oder alphanumerisch machen
umfang = 2 * ( laenge + breite )
flaeche = laenge * breite
print( "Das Rechteck mit der laenge ", laenge, "cm und der Breite ", breite, "cm hat einen Umfang (U) von ", umfang, "cm und eine Flaeche (A) von ", flaeche, "qcm" )
user@host:>

Ein sehr einfach gehaltenes Programm, das nur dazu dient, die builtin-function eval() kurz vorzustellen.
Ich finde es so praktisch, dass man die *Auswertung* der Eingabe einer builtin-function übergeben kann.

Wir wissen ja schon, dass via input() ein Objekt vom Typ str zurückkommt, welches eval() übergeben bekommt.
eval() nimmt also ein Objekt vom Typ str entgegen. Optional kann man aber auch globals und locals als zweites/drittes Argument dazuangeben.

eval( expression-argument, globals=None, locals=None)

globals muss ein dict-objekt sein (zu dict(ionary) siehe später in basics), locals muss ebenfalls ein mapping-Objekt sein; hat mit namespaces zu tun -
aber lassen wir das einmal außer acht

Das expression-argument wird von eval geparsed bzw. ausgewertet. zum Beispiel:

>>> bin(73)
'0b1001001'
>>> hex(893422)
'0xda1ee'
>>> print ('0o723' )
0o723
>>> eval( '0b1001001+ 0xda1ee /0o723')
1986.1092077087794
>>>

VORSICHT FALLGRUBE!!
eval( input( ... ))
macht Sinn, wenn ich (eher seltener) einen Ausdruck auswerten möchte, oder sicherstellen will, dass ein int/float übergeben wird.
Soll aber ein str()-Objekt bei input() (was ja der default ist) ohnehin zurückgegeben werden, dann ist

>>> name = eval( input( 'Ihr Name: '))
Ihr Name: Franz
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'Franz' is not defined
>>> name = eval( input( 'Ihr Name: '))
Ihr Name: 'Franz'
>>> print( name )
Franz
>>> name = input( 'Ihr Name: ' )
Ihr Name: Franz
>>> print( name )
Franz
>>>

Also darauf acht geben! Wir werden an manchen Stellen eval() wieder begegnen.

tuple

>>> ein_tuple = (1,2,3,)
>>> zweites_tuple = (1)
>>> print( zweites_tuple )
1
>>> print( type( zweites_tuple ) )
<class 'int'>
>>> zweites_tuple = (1,)
>>> print( type( zweites_tuple ) )
<class 'tuple'>
>>> print( zweites_tuple )
(1,)
>>> '2' in ein_tuple
False
>>>

Die runden Klammern kennzeichnen das tuple-Objekt.
Der Beistrich am Ende, vor der schließenden runden Klammer, garantiert, dass ein tuple gebaut wird.
Es ginge auch sowas:

>>> drittes_tuple = 1,
>>> print( type( drittes_tuple ) )
<class 'tuple'>
>>> viertes_tuple = 'a', 'b', 'c',
>>> print( type( viertes_tuple ) )
<class 'tuple'>
>>>

Der finale Beistrich ist bei der Angabe EINES Objekts im tuple zwingend notwndig, soll ein tuple gebaut werden - bei mehr als einem Objekt ist der finale Beistrich tatsächlich optional; wenn Du das genauer überlegst, dann wird dir schnell ein Licht aufgehen; es kommt immer drauf an, auf was Du zeigen lassen willst.

>>> print( viertes_tuple[2] )
c
>>> viertes_tuple[2] = 'uu'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>>

tuple sind immutable - daher sind tuple im Vergleich zu Listen weniger speicheraufwendig.
Verändern könnte man tuple (auch bei strings), indem man die Objekte zerreißt (mit Hilfe der slicing-Technik) und in einem neuen tuple-Objekt (oder str-Objekt) zusammensetzt.

Sonderfall:
nur [:] macht eine 1:1-Kopie im Arbeitsspeicher von einer (muteable) Liste - bei strings oder tuple brauche ich das nicht (wenn ich es verändere, wird sowieso ein neues Objekt gebaut).

>>> vor = 'michael'
>>> nach = vor[:]
>>> print( id( vor ) )
3077467712
>>> print( id( nach ) )
3077467712
>>> T = 1,2,3,
>>> TT = T[:]
>>> print( id( T ) )
3076383964
>>> print( id( TT ) )
3076383964
>>> L = [1,2,3,4]
>>> LL = L[:]
>>> print( id( L ) )
3077467948
>>> print( id( LL ) )
3076407564
>>>

Das ist interessant, wenn sich etwas im Objekt, auf das der Name L zeigt, ändert.
Lassen wir A noch auf das Objekt mit id 3077467948 zeigen, und ändern eine Kleinigkeit

>>> A = L
>>> L[2] = 'u'
>>> print( L )
[1, 2, 'u', 4]
>>> print( A )
[1, 2, 'u', 4]
>>> print( LL )
[1, 2, 3, 4]
>>>

klingt nach eh klar, gehörte aber extra erwähnt (Fallgrube, wenn man durch liste iteriert und in der Iteration (for-Schleife) Änderung in der Liste vornimmt).
Man nennt das auch flache Kopie (shallow copy) - es gibt dann noch eigene copy()-Funktionen.
Dazu später mehr (copy-Modul) (aßerhalb des basics-Bereiches).

list

Listen erinnern uns an arrays; L[0], L[5], usw. Aufgepasst! Man beginnt bei 0!
Die eckigen Klammern [] sind ihr Markenzeichen und verursachen eine Art Wiedererkennungseffekt in uns.
Listen sind muteable.
was heißt das?

Werden Listen verändert (neue Objekte kommen dazu, werden aus der Liste gelöscht, oder werden verändert), so passiert das in place (im Hauptspeicheradressbereich des list-Objekts) - die Objekt-id der Liste, die eine Änderung erfuhr, hat sich NICHT geändert.
Das geht bei Objekten vom Typ tuple nicht, wie wir noch sehen werden.
tuple und str sind immuteable - Willst das (immuteable) Objekt ändern, dann musst Du ein neues Objekt (aus bestehenden) *bauen*, wobei das bestehende unverändert bleibt.

>>> eine_liste = [ 0, 'a', 2, [1,2,3], 'nico']
>>> print( len( eine_liste ) ) #soviele Objekte in Liste
5
>>> print( type( eine_liste ) )
<class 'list'>
>>> print( eine_liste[2] )
2
>>> print( eine_liste[-1] )
nico
>>>

Die Objekte werden an gewissen Positionen in der Liste angemacht. Stellen wir uns die Liste als Wäscheleine vor, dann hängt das erste Objekt an Kluppe 0, und bevor ein Objekt an Kluppe 3 angemacht werden kann, muss ein Objekt an Kluppe 1 und Kluppe 2 hängen.
Um die Reihenfolge des Auffüllens brauchen wir uns aber nicht kümmern, denn das macht der Interpreter für uns.
Und so konntne wir nachsehen, was wieviele Sachen auf der Wäscheliene hängen (len(eine_liste), welcher Art die Wäscheleine ist (und davon können wir ja auch die Regeln ableiten, denen der Objekttyp unterworfen ist) type(eine_liste), und schlißslich konnten wir nachsehen, welches objekt an Kluppe 2 (das dritte von links) hängt.
print (eine_liste[2]).
print (eine_liste[-1]) ist sehr praktisch, wenn man sich das Objekt näher ansehen möchte, das am weitesten rechts hängt.

>>> eine_liste[1] = 1
>>> print( eine_liste )
[0, 1, 2, [1, 2, 3], 'nico']
>>> print( id(eine_liste[1] ) )
137068832
>>> print( id( eine_liste[3][0] ) )
137068832
>>> 'nico' in eine_liste
True
>>>

Die beiden 1er in der Liste sind das EINE int-Objekt 1 (1 gibt es als Objekt nur ein Mal im Hauptspeicher, und die id kann das ja beweisen).
Das list-Objekt hat natuerlich auch seine eigene Object-id

>>> print( id( eine_liste ) )
3076895788
>>> eine_liste[3] = 'drei'
>>> print( id( eine_liste ) )
3076895788
>>>

Die Object-id der Liste blieb, wie erwartet, unverändert. Die Änderung im Arbeitsspeicher passierte in place der Speicheradresse des list-Objekts.

leere Liste

Manchmal braucht man schnell eine leere Liste - also eine Liste, in der nix drinnen ist. Zwei Mölichkeiten stehen uns hier zur Verfügung:

>>> leer1 = []
>>> leer2 = list()
>>>

Um einen Listeneintrag zu verändern, braucht man nur die Kluppennummer angeben, damit der Interpreter weiß, welches Objekt durch was ersetzt werden soll.

>>> eine_liste = [ 0, 'a', 2, [1,2,3], 'nico']
>>> print( id( eine_liste ) )
3083545164
>>> eine_liste[1] = 'Z'
>>> print( id( eine_liste ) )
3083545164
>>> print( eine_liste )
[0, 'Z', 2, [1, 2, 3], 'nico']
>>>

Das war hier schön zu sehen. Möchte man ein Objekt herauslöschen, verwenden ir vorerst del.

>>> del eine_liste[1]
>>> print( eine_liste )
[0, 2, [1, 2, 3], 'nico']
>>>

Um ein Objekt in die Liste zu bekommen, ist gar nicht so trivial. Ich kann nicht einfach so *sagen*, dass er, der Interpreter, mir etwas an die Stele zwischen 2. und 3. kluppe einbaut, sodass sich die Nummerierung ab der dritten Kluppe um eins erhöht, indem ich
eine_liste[2] = 'neu'
tippe. In diesem Fall würde ja kein neues Objekt eingefügt, sondern das bestehende an der 4. Kluppe (indexPosition 2) ersetzt werden.
Wir müssen hier auf eine sogenannte Methode an einem list-Objekt zurückgreifen, die ich ier nicht so ausführlich beschreiben m&oul;chte, denn das passiert unter dem Punkt Arbeiten mit Listen.
Lasst uns also neu zwischen Kluppe zwei und Kluppe drei hängen.

>>> eine_liste.insert(2, 'neu')
>>> print( eine_liste[2] )
neu
>>> print( eine_liste[1] )
2
>>> print( eine_liste[3] )
[1, 2, 3]
>>>

Wir sahen, wie wir etwas IN unsere Liste aufnehmen können (insert()), und wir sahen auch, dass die Kluppennummer sich um eins erhöht ab jener Kluppe, die rechts vom neuen Objekt in der Liste steht. Da beim Einfügen in eine bestehende Liste einige Objekte verschoben werden, also, um bei unserem Bild zu bleiben, von Kluppe x runtergenommen und auf Kluppe x+1 heften, sobald diese frei ist, denn das Objekt auf Kluppe x+1 muss ja vorher auf Kluppe x+2 landen. Und deshalb ist darauf zu achten, dass man insert-Methoden nur im Notfall einsetzt. Man kommt auch ganz ut ohne aus.

append() ist, zum Beispiel, so eine Methode, mit der man Objekte in eine Liste bringt.

>>> L = list()
>>> L[0] = 'kracht'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list assignment index out of range
>>> L.append('krachtnicht')
>>> L.append(
... ['a','b','c'])
>>> L.append('passt')
>>> print( L )
['krachtnicht', ['a', 'b', 'c'], 'passt']
>>> 

Wir konnten also noch immer nicht (und werden es auch nie können) ein Objekt an der Stelle 0 in ein leeres Listenobjekt einfügen. Aber hinten dranhängen, das war sehr einfach.

extend() ist eine sehr ähnliche Methode; ABER
aber das Objekt muss (durch)iterierbar sein (siehe for-Schleife).

>>> L.extend(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
>>> L.extend((1,))
>>> L.extend([2,3,4])
>>> L.extend('hurra')
>>> L.extend(2)
Traceback (most recent call last):
  File ">stdin&g;t", line 1, in <module>
TypeError: 'int' object is not iterable
>>>

Frei nach dem Motto, dass man alles probieren darf, weil einem der Interpreter ohnehin eine Exception raised, probierte ich gleich am Anfang, eine einfache int-Zahl zu übergeben. Er schrie sofort, dass das eben kein iterierbares Objekt wäre, daher der Trick mit (1,).
Lass uns 'mal 'reinschauen, was denn nun wo in der Liste steht!?

>>> print( L )
['krachtnicht', ['a', 'b', 'c'], 'passt', 1, 2, 3, 4, 'h', 'u', 'r', 'r', 'a']
>>>

Jetzt ist es klar - extend() erweitert nicht die Liste um ein weiteres Listenobjekt, sondern läßt die Objekte der Liste in die Zielliste von hinten hineinrieseln.
Und wir beobachteten, dass das Einfügen der Daten auch hinten (Memory-kostengünstiger) passiert.
bezeichnend auch das extenden der Liste mit einem string - dieser wird zuerst in seine Einzelteile zerlegt, und kommt so in die Liste rein, wie wenn man durch die sequence durchiteriert und die einzelnen Characters einzeln angehängt hätte.

Natuürlich kann man Listen einfach addieren.

>>> L2 = ['zwei','vonzwei',2]
>>> L3 = L2 + L
>>> print( L3 )
['zwei', 'vonzwei', 2, 'krachtnicht', ['a', 'b', 'c'], 'passt', 1, 2, 3, 4, 'h', 'u', 'r', 'r', 'a']
>>>

Wie der Name der count-Methode schon verrät, gibt diese Methode Auskunft, wie oft ein Objekt in der Liste vorkommt

>>> print( "Das Objekt 2 kommt in der Liste L3 {} Mal vor".format( L3.count( 2 ) ) )
Das Objekt 2 kommt in der Liste L3 2 Mal vor
>>>

wohingegen die index-Methode am list-Objekt die Position des ersten Auftretens des Objekts in der Liste zurückwirft

>>> print( L3.index( 2 ) )
2
>>> print( L3.index( 2, 3 ) )
7
>>>

Durch Angabe eines zusätzlichen Arguments konnte auch die Position eines weiteren Objekts 2 aufgespürt werden. Wird bei index() ein Objekt anggeben, das nicht in der Liste ist, dann bekommt man eine ValueError-Exception vor den Latz geknallt.

>>> print( L3.index( 2, 3 ) )
7
>>> L3.index('X')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: 'X' is not in list
>>>

Aha - hier gilt es aufzupassen. Der ValueError wird immer ausgespuckt, wenn eine index-Position wegen des NichtVorhandenSeins des Objekts kein Ergebnis liefern kann.
Gibt man nun bei der index()-Methode einen zweiten Wert an, dann sagt man dem Interpreter, dass man ab hier die Index-Position des darauffolgenden (gesuchten) Objekts wissen möchte.
Wenn diese Abfrage einen Exception raised, dann bedeutet das nur, dass die agesetzte Anfrage kein Resultat ergab.
Wenn das Objekt an 2. Position vorkommt, und ich noch eine Abfrage starte ab IndexPosition 5 bis zum Ende der Liste, und es gibt aber kein gleiches Objekt mehr bis zum Schluss, dann bekommen wir diesen ValueError ebenfalls. Daher nicht automatisch davona ausgehen, dass es das Objekt in der Liste nicht gibt, wenn man diese Exception geraised bekommt!

bytestring

bytestrings (byte, 1byte = 8bit, 2hoch8==256) ist eine Folge (sequence) von Zahlen von 0 bis 255.
So ein bytestring sieht aus wie ein string, dem ein b (oder auf anderen Platformen ein B) vorangestellt wurde.
Mit der built-in-function bytes() kann man leicht aus einem str-Objekt einen bytestring bauen.

>>> b = bytes(a,'utf-8')
>>> print( type( b ) )
<class 'bytes'>
>>> print( b )
b'michael'
>>>

Es geht aber auch anders

>>> help( ''.encode )
Help on built-in function encode:

encode(...)
    S.encode(encoding='utf-8', errors='strict') -> bytes
    
    Encode S using the codec registered for encoding. Default encoding
    is 'utf-8'. errors may be given to set a different error
    handling scheme. Default is 'strict' meaning that encoding errors raise
    a UnicodeEncodeError. Other possible values are 'ignore', 'replace' and
    'xmlcharrefreplace' as well as any other name registered with
    codecs.register_error that can handle UnicodeEncodeErrors.
(END)
>>> c = a.encode('utf-8')
>>> type( c )
<class 'bytes'>
>>> c
b'michael'
>>>

Und wie kann man aus einem Objekt vom Typ bytes ein Objekt vom Typ str bauen?

>>>  help(b''.decode)
...
>>> s = c.decode()
>>> print( s )
michael
>>>

Aber hey - gibt es denn da nicht auch die str()-built-in-function?
Warum kann ich nicht die heranziehen, um den bytestring in einen herkömmlichen string umzuwandeln. Bei int-Objekten ging das doch auch!
schauma'mal

>>> s2 = str(c)
>>> type( s2 )
<class 'str'>
>>> print( s2 )
b'michael'
>>> s2
"b'michael'"
>>>

OUPS - str() wandelt mir die Ausgabe des bytestrings mitsamt dem vorangestellten b in einen string um - DAS war nicht das, was wir wollten, und daher verwenden wir hierfür decode()!

Wie strings sind auch bytestrings immuteable (unveränderbar.

Mit Hilfe der bytes()-Funktion kann man aus einer Liste von Zahlen (0 bis 255) einen bytestring erzeugen.

>>> chr( 97 ),chr( 98 ),chr( 99 ),chr( 100 ),
('a', 'b', 'c', 'd')
>>> bytes( [ 97, 98, 99, 100 ] )
b'abcd'
>>>

Was wenn wir uns nicht an die Vorgabe halten (0 - 255)?

>>> krachbum = bytes([ 960 ]) #fuer PI
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: bytes must be in range(0, 256)
>>>

Prompt bekamen wir die Rechnung präsentiert. bytes must be in range(0, 256)
noch ein anderes Beispiel

>>>  b = bytes( [ 100, 102, 128, 130 ] )
>>> print( b )
b'df\x80\x82'
>>>

ASCII (American Standard Code for Information Interchange) codiert Buchstaben, Zahlen und Satzzeichen (insgesamt 95 druckbare Zeichen) durch Nummern zwischen 0 und 127. Wenn nun für eine Zahl (wie 128 oder 130) kein ASCII-Zeichen existiert, dann erscheint an deren Stelle eine ESCAPE-Sequenz, die die Zahl als Hexadezimalzahl wiedergibt (\x80\x82).

Man kann im bytestring auf einzelne Werte zugreifen. Allerdings erhält man kein Zeichen, sondern die Zahl.

>>>  s = b'abcdef'
>>> s[2],s[4]
(99, 101)
>>> chr( 99 )
'c'
>>>

bytearray

Ein bytearray ist eine Liste aus Zahlen von 0 bis 255.
Im Unterschied zum bytestring sind bytearrays muteable (veränderbar.

>>> ba = bytearray( [ 97, 98, 99, 100 ] )
>>> type( ba )
<class 'bytearray'>
>>> print( ba )
bytearray(b'abcd')
>>>

Die Ausgabe von print() zeigt, dass der bytestring (b' ') noch in bytearray() eingefasst wird. Die items eines bytearrays werden nach Möglichkeit als ASCII-Zeichen dargestellt (wie beim bytestring).


Hier geht es zum Seitenanfang und da geht es zur python-Startseite