Live Hacking¶
Strings können viel: split()
¶
Beim zeilenweisen Lesen eines Files, die Nutzdaten einer Zeile extrahieren wollend … * Frage: wie spalte ich eine Zeile, die offensichtlich mit Tabs separierte Felder hat? * Antwort: `split()
ohne Argumente <https://docs.python.org/3/library/stdtypes.html#str.split>`__ ist am gscheitesten, weil es aufeinanderfolgende Whitespaces als eines nimmt.
[80]:
line = ' \t abc \t def '
line.split()
[80]:
['abc', 'def']
Tuple unpacking ist praktisch, wenn man weiss, dass z.B. genau drei Felder rauskommen werden …
[81]:
line = 'aaa\tbbb\tccc'
feld0, feld1, feld2 = line.split()
print(feld0, feld1, feld2)
aaa bbb ccc
Gibts optionale Felder in der Zeile - aber ganz sicher mindestens drei -, so splittet man drei weg, und bearbeitet den Rest manuell nach …
[82]:
# optional fields
line = 'aaa\tbbb\tccc ddd eee'
feld0, feld1, feld2, rest = line.split(maxsplit=3)
print('feld0:', feld0, ', feld1:', feld1, ', feld2:', feld2, ', rest:', rest)
restfields = rest.split()
if len(restfields):
print('do is no wos dabei')
feld0: aaa , feld1: bbb , feld2: ccc , rest: ddd eee
do is no wos dabei
Geht auch von rechts her …
[83]:
line = '1.2.3.4.10101010'
rest, switchnumber = line.rsplit('.', maxsplit=1)
[84]:
print(rest, switchnumber)
1.2.3.4 10101010
Mutability, Immutability: kann man nicht oft genug wiederholen¶
[85]:
l1 = [1,2,3]
l2 = l1
print(l1)
print(l2)
[1, 2, 3]
[1, 2, 3]
[86]:
l1.append('vier')
l1
[86]:
[1, 2, 3, 'vier']
Das ist der Punkt: die Änderung sieht man auch über l2
[87]:
l2
[87]:
[1, 2, 3, 'vier']
l1
und l2
zeigen auf dasselbe Objekt:
[88]:
print(id(l1))
print(id(l2))
140070310441600
140070310441600
Dass die beiden auf das gleiche Objekt zeigen, ist nicht so das Problem. Das Problem ist eigentlich, dass das Objekt verändert werden kann.
list
ist mutable -> Problemtuple
ist immutable -> kein Problem
[89]:
t1 = (1,2,3)
t1
[89]:
(1, 2, 3)
[90]:
t2 = t1
t2
[90]:
(1, 2, 3)
[91]:
print(id(t1))
print(id(t2))
140070371015728
140070371015728
[92]:
try:
t1.append('vier')
except AttributeError:
print('gottseidank sind tuples immutable')
gottseidank sind tuples immutable
Dictionaries¶
[93]:
d = {'eins': 1,
'zwei': 2
}
d['eins']
[93]:
1
[94]:
print(type(d))
<class 'dict'>
Alternativer dict
Aufruf: Liste von (key,value)
Paaren
[95]:
d = dict([('eins', 1), ('zwei', 2)])
“Hartes” Indizieren …¶
[96]:
d['eins']
[96]:
1
Fehlender Key -> Exception
[97]:
try:
d['drei']
except KeyError as e:
print('nix key in dict:', e)
nix key in dict: 'drei'
“Weiches” Nettfragen …¶
[98]:
d.get('eins')
[98]:
1
[99]:
value = d.get('drei')
print(value)
None
Shortcuts¶
[100]:
# umstaendlich
value = d.get('hundert')
if value is None:
value = 100
print(value)
100
[101]:
# einfach
value = d.get('hundert', 100)
print(value)
100
[102]:
d
[102]:
{'eins': 1, 'zwei': 2}
[103]:
# umstaendlich
value = d.get('hundert')
if value is None:
d['hundert'] = 100
value = 100
print(value)
100
[104]:
d
[104]:
{'eins': 1, 'zwei': 2, 'hundert': 100}
[105]:
# (clear this up)
del d['hundert']
[106]:
# einfach
value = d.setdefault('hundert', 100)
print(value)
100
[107]:
d
[107]:
{'eins': 1, 'zwei': 2, 'hundert': 100}
Objektorientierte Programmierung¶
[108]:
class MeinVoelligSinnloserTyp:
def __init__(self):
self.eins = 1
self.zwei = 2
def sinnlose_addition(self):
return self.eins + self.zwei
[109]:
sinnlobj = MeinVoelligSinnloserTyp()
[110]:
sinnlobj.eins
[110]:
1
[111]:
sinnlobj.zwei
[111]:
2
[112]:
sinnlobj.drei = 3
[113]:
sinnlobj
[113]:
<__main__.MeinVoelligSinnloserTyp at 0x7f64a8934890>
[114]:
sinnlobj.drei
[114]:
3
[115]:
sinnlobj.sinnlose_addition()
[115]:
3
Inheritance¶
[116]:
class NochWenigerSinnvoll(MeinVoelligSinnloserTyp):
def __init__(self, der_absolute_unsinn):
super().__init__()
self.unsinn = der_absolute_unsinn
def uebertreibs_jetzt_bitte(self):
self.eins += self.unsinn
self.zwei += self.unsinn
[117]:
bs = NochWenigerSinnvoll(666)
[118]:
bs
[118]:
<__main__.NochWenigerSinnvoll at 0x7f64a894a790>
[119]:
bs.sinnlose_addition()
[119]:
3
[120]:
bs.uebertreibs_jetzt_bitte()
[121]:
bs.sinnlose_addition()
[121]:
1335
Date and Time and Time Deltas - datetime
¶
[122]:
import datetime
now = datetime.datetime.now()
now
[122]:
datetime.datetime(2020, 3, 17, 13, 55, 2, 819508)
[123]:
uptime = datetime.timedelta(seconds=358)
uptime
[123]:
datetime.timedelta(seconds=358)
[124]:
boot = now - uptime
boot
[124]:
datetime.datetime(2020, 3, 17, 13, 49, 4, 819508)
Bissl potschert zum Schreiben is scho … (andere Formen von import
)¶
[125]:
from datetime import datetime, timedelta
irgendwann_einmal_gewesen = datetime.now() - timedelta(days=15)
irgendwann_einmal_gewesen
[125]:
datetime.datetime(2020, 3, 2, 13, 55, 2, 834972)
[126]:
from datetime import datetime as datetime_from_batteries
datetime_from_batteries.now()
[126]:
datetime.datetime(2020, 3, 17, 13, 55, 2, 840837)
String Representations: __str__()
¶
[127]:
# recover from the datetime massacre we created above
del datetime
import datetime
now = datetime.datetime.now() # for example
str(now)
[127]:
'2020-03-17 13:55:02.846889'
Frage: woher weiss str()
, datetime
als String ausschauen will?
[128]:
class Sinnlos:
def __init__(self, sinnlos):
self.sinnlos = sinnlos
def __str__(self):
return 'sinnlose str repraesentation von {}'.format(self.sinnlos)
str(Sinnlos(666))
[128]:
'sinnlose str repraesentation von 666'
[129]:
str(Sinnlos([1,2,3,'vier']))
[129]:
"sinnlose str repraesentation von [1, 2, 3, 'vier']"
subprocess
¶
[130]:
import subprocess
subprocess.run(['ls', '-l'])
[130]:
CompletedProcess(args=['ls', '-l'], returncode=0)
Nicht viel Information: returncode = 0
-> ls erfolgreich beendet * Frage: Was ist mit dem Output? * Antwort: schau in dem Terminal nach, das du verwendest, um dieses Notebook anzuzeigen. (Die Cells werden vom Browser an den Notebook-Server gesendet, der sie dann ausführt.) Hmm … * Frage: und wie krieg ich den von dort weg, in mein Programm rein? * Antwort: so ->
[131]:
result = subprocess.run(['ls', '-l'], capture_output=True)
print(result.stdout)
b'total 52\ndrwxrwxr-x. 2 jfasch jfasch 4096 Mar 17 13:48 Exercises\n-rw-r--r--. 1 jfasch jfasch 2607 Mar 17 13:48 index.rst\n-rw-rw-r--. 1 jfasch jfasch 33768 Mar 17 13:54 LiveHacking.ipynb\ndrwxrwxr-x. 2 jfasch jfasch 4096 Mar 17 13:48 Misc\n-rw-r--r--. 1 jfasch jfasch 0 Mar 17 13:48 README.rst.~1~\ndrwxr-xr-x. 4 jfasch jfasch 4096 Mar 17 13:48 SwitchZeug\n'
Hmm … das ist jetzt kein String, sondern Bytes (erkennbar am ‘b’). Keiner weiss, in welchem Encoding ls -l
seinen stdout
formuliert - also ist das nur logisch so.
[132]:
print(type(result.stdout))
<class 'bytes'>
Frage: Wenn ich irgendwas mit dem Output machen will?
Antwort: Dann konvertier es in einen String. Dabei musst du dich aber aufs Encoding festlegen.
[133]:
stdout_as_utf8 = str(result.stdout, encoding='utf-8')
print(type(stdout_as_utf8))
<class 'str'>
[134]:
stdout_as_utf8
[134]:
'total 52\ndrwxrwxr-x. 2 jfasch jfasch 4096 Mar 17 13:48 Exercises\n-rw-r--r--. 1 jfasch jfasch 2607 Mar 17 13:48 index.rst\n-rw-rw-r--. 1 jfasch jfasch 33768 Mar 17 13:54 LiveHacking.ipynb\ndrwxrwxr-x. 2 jfasch jfasch 4096 Mar 17 13:48 Misc\n-rw-r--r--. 1 jfasch jfasch 0 Mar 17 13:48 README.rst.~1~\ndrwxr-xr-x. 4 jfasch jfasch 4096 Mar 17 13:48 SwitchZeug\n'
[135]:
stdout_as_utf8.split('\n')
[135]:
['total 52',
'drwxrwxr-x. 2 jfasch jfasch 4096 Mar 17 13:48 Exercises',
'-rw-r--r--. 1 jfasch jfasch 2607 Mar 17 13:48 index.rst',
'-rw-rw-r--. 1 jfasch jfasch 33768 Mar 17 13:54 LiveHacking.ipynb',
'drwxrwxr-x. 2 jfasch jfasch 4096 Mar 17 13:48 Misc',
'-rw-r--r--. 1 jfasch jfasch 0 Mar 17 13:48 README.rst.~1~',
'drwxr-xr-x. 4 jfasch jfasch 4096 Mar 17 13:48 SwitchZeug',
'']
[136]:
# fuer ls gibts was besseres
import os
for elem in os.listdir('.'):
print(elem)
.ipynb_checkpoints
Misc
SwitchZeug
index.rst
LiveHacking.ipynb
README.rst.~1~
Exercises
.gitignore
[137]:
# noch besser: rekursives Iterieren!!
for dirpath, dirnames, filenames in os.walk('.'):
print('dirpath:', dirpath, ', dirnames:', dirnames, ', filenames:', filenames, '\n')
dirpath: . , dirnames: ['.ipynb_checkpoints', 'Misc', 'SwitchZeug', 'Exercises'] , filenames: ['index.rst', 'LiveHacking.ipynb', 'README.rst.~1~', '.gitignore']
dirpath: ./.ipynb_checkpoints , dirnames: [] , filenames: ['LiveHacking-checkpoint.ipynb', 'ITG-Project-checkpoint.ipynb']
dirpath: ./Misc , dirnames: [] , filenames: ['scope.py', 'getter-setter.py', 'eval-argv.py', 'fibo-generator-yield.py', 'listcomprehension-generatorexpression.py', 'operator-overloading.py', 'scope-closure.py']
dirpath: ./SwitchZeug , dirnames: ['SwitchZeug', 'data'] , filenames: ['01-der-anfang.py', 'switchdb-create-schema.py', 'switchdb-report-switch.py', 'tests.py', 'michi-switch-upgrade-von-ini.py', 'michi-switch-upgrade-config.txt', '02-michi-parse.py', 'switchdb-insert-switch.py', 'michi-switch-upgrade-von-pyexec.py', 'tests.py.~1~', 'michi-switch-upgrade-config.ini']
dirpath: ./SwitchZeug/SwitchZeug , dirnames: [] , filenames: ['interface.py', 'switch.py', 'error.py', 'database.py.~1~', 'michi.py', 'database.py', '__init__.py']
dirpath: ./SwitchZeug/data , dirnames: [] , filenames: ['ifAdminStatus.txt', 'ifDescr.txt', 'ifLastChange.txt', 'snmpEngineTime.txt', 'ifOperStatus.txt']
dirpath: ./Exercises , dirnames: [] , filenames: ['20.py', 'prime.py', 'uniq.py', 'digit.py']
enumerate()
, zip()
¶
Feine kleine Helferlein … (hochtrabend nennt sich das “Funktionales Programmieren”) * `enumerate()
<https://docs.python.org/3/library/functions.html#enumerate>`__ * `zip()
<https://docs.python.org/3/library/functions.html#zip>`__ * Mehr davon
[138]:
l = [200, 300, 400]
for num, elem in enumerate(l, start=1):
print(num, elem)
1 200
2 300
3 400
[139]:
l1 = [1,2,3]
l2 = ['eins', 'zwei', 'drei']
for links, rechts in zip(l1, l2):
print(links, rechts)
1 eins
2 zwei
3 drei
List Comprehension¶
[140]:
# umstaendlich
l = [1,2,3,4]
l_quadrat = []
for n in l:
l_quadrat.append(n**2)
print(l_quadrat)
[1, 4, 9, 16]
[141]:
# einfach
l_quadrat = [n**2 for n in l]
print(l_quadrat)
[1, 4, 9, 16]
exec()
, eval()
¶
Frage: (Michi) mein Programm hat eine komplizierte Konfiguration (Anm.: assoziiert Firmware-Versionen mit Files, und macht Updates basierend darauf), die im Programm aufgebaut wird. Wie kann ich die Konfiguration aus dem Programm rausnehmen und die Python-Variablen von einem Konfigurationsfile lesen?
Antwort:
`eval()
<https://docs.python.org/3/library/functions.html#eval>`__ und`exec()
<https://docs.python.org/3/library/functions.html#exec>`__Siehe Beispiel mit ``exec()` <SwitchZeug/michi-switch-upgrade-von-pyexec.py>`__
Siehe Beispiel mit ``configparser`: Windows .ini <SwitchZeug/michi-switch-upgrade-von-ini.py>`__
[142]:
expression = '1 == 2'
eval(expression)
[142]:
False
[143]:
liste_als_string = '[1,2,3]'
[144]:
liste = eval(liste_als_string)
liste
[144]:
[1, 2, 3]
Umgebender Context¶
[145]:
herbert = 666
[146]:
eval('herbert') # Variable 'herbert' liegt heraussen
[146]:
666
[147]:
exec('joerg = 42') # Variable 'joerg' wird heraussen definiert
[148]:
joerg
[148]:
42
Eigener Context¶
[149]:
eval('joerg*3', {'joerg': 666})
[149]:
1998
[150]:
del joerg # cleanup what I did above
[151]:
context = {}
exec('joerg = 42', context)
[152]:
context['joerg']
[152]:
42
json
¶
Das ist das Leichteste was es gibt …
[153]:
mein_dict = {
'joerg': 666,
'gerhard': {
'age': 32,
'size': 175,
}
}
[154]:
import json
dict_as_json_string = json.dumps(mein_dict)
dict_as_json_string
[154]:
'{"joerg": 666, "gerhard": {"age": 32, "size": 175}}'
[155]:
gelesenes_dict_von_json = json.loads(dict_as_json_string)
gelesenes_dict_von_json
[155]:
{'joerg': 666, 'gerhard': {'age': 32, 'size': 175}}