python michael poeltl © 2011-2015

Die Standard Library (STL)

Überblick:

Einleitung

Die STL besteht aus vielen packages und Modulen, die mit der installierten python-Version mitkommen.
Hier finden sich jene, die mir dort und da untergekommen sind, und ich erhebe da keinen Anspruch auf eine ausführliche makellose Erklärung. Dafür sind die doc-Seiten zur jeweiligen python-Version da.

pydoc

Mit Hilfe von pydoc (oder pydoc3) kann man sich die manpage zu einem Modul/package auf der commandline aufrufen, ohne dass man den python-Interpreter aufrufen und dann das jeweilige Modul importieren muesste.
Das betrifft Module/packages, die freilich nur über sys.path auch erreichbar sind.

user@host:> /opt/bin/pydoc3 sys
Help on built-in module sys:

NAME
    sys

    MODULE REFERENCE
        http://docs.python.org/3.4/library/sys
...

Coole Sache!

copy
for deep copies

>>> import copy
>>> L = ['a', 'b', [3,5,7]]
>>> L2 = copy.deepcopy(L)
>>> L[0] = 'passt'
>>> print (L2[0])
a
>>> print (L[0])
passt
>>> L[2][0] = 'test'
>>> print (L2[2][0])
3
>>> print (L[2][0])
test
>>>

decimal

Wir hatten in den basics schon davon gehö, dass floats (Gleitpunktzahlen) oft nicht genau mittels *Einsen-und-Nullen* (also binär) abgebildet werden können. Somit kann nur ein gewisses Maß an Genauigkeit (acht oder neun Nachkommastellen) garantiert werden. Und mit diesem Problem haben freilich alle Codierer in ihrer Programmiersprache zu kämpfen, ist also keine python-spezifisches Besonderheit.

In python kann das Modul decimal zu einer viel höheren Genauigkeit (arbitrary precision) verhelfen. Das Modul beinhaltet (als zentrales Element) eine Klasse Decimal. Mit Objekten diesen Typs lassen sich dann Berechnungen durchführen.

>>> import decimal
>>> sum( decimal.Decimal("0.1") for i in range(10)) == decimal.Decimal("1.0")
True
>>> decimal.Decimal('0.3') + decimal.Decimal('7.8') - decimal.Decimal('3.77')
Decimal('4.33')
>>> a = decimal.Decimal('7.4')
>>> b = decimal.Decimal('-3.9')
>>> c = decimal.Decimal('10.00234')
>>> max(a,b,c)
Decimal('10.00234')
>>> min(a,b,c)
Decimal('-3.9')
>>> d = decimal.Decimal('23.2e05')
>>> float(d)
2320000.0
>>> decimal.Decimal(0.1) + decimal.Decimal(0.1) + decimal.Decimal(0.1) - decimal.Decimal(0.3)
Decimal('2.775557561565156540423631668E-17')
>>> decimal.Decimal('0.1')
Decimal('0.1')
>>> decimal.Decimal('0.1') + decimal.Decimal('0.1') + decimal.Decimal('0.1') - decimal.Decimal('0.3')
Decimal('0.0')
>>> 0.1+0.1+0.1-0.3
5.551115123125783e-17
>>>

OK - damit haben wir hinreichend gezeigt, dass Binärzahlen, die das System intern verwendet, Gleitpunktzahlen sind, die oft nur näherungsweise die eigentliche Zahl darstellen können.

Man übergibt als (ersten) Parameter einen String, der die Gleitpunktzahl repräsentiert, und behandelt des Decimal-Objekt, als wäre es eine *normale* Gleitpunkt-Zahl. Cool!

>>> import math
>>> math.sqrt(2)
1.4142135623730951
>>> decimal.Decimal('2').sqrt()
Decimal('1.414213562373095048801688724')
>>> decimal.Decimal(2).sqrt()
Decimal('1.414213562373095048801688724')
>>>

Rechnungen mit DezimalObjekten findenimmer in einem Kontext statt, der durch ein Objekt der Klasse Context repräsentiert wird.
Für jeden Thread wird automatisch ein Kontext mit default-Werten erzeugt.

>>> import decimal
>>> print( decimal.getcontext() )
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[],
traps=[InvalidOperation, DivisionByZero, Overflow])
>>>

Wenn man mit dem einen oder anderen default-Wert gerade nicht zufrieden ist, dann kann man den folgendermaßen ändern:

>>> mycontext = decimal.Context(prec=3)
>>> decimal.getcontext()
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])
>>> decimal.setcontext(mycontext)
>>> decimal.getcontext()
Context(prec=3, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])
>>> decimal.setcontext( decimal.Context( prec=3, capitals=0 ))
>>> decimal.getcontext()
Context(prec=3, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=0, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])
>>> decimal.Decimal('1') / decimal.Decimal('3')
Decimal('0.333')
>>>

Bei Näherungsverfahren zu irrationalen Zahlen stelle ich mir das sehr hilfreich vor. Ich denke da gerade an die Zahl Epsilon (Goldener Schnitt) oder Näherungsverfahren zur Irrationalen Zahl PI.

der flags-Parameter zeigte ein leeres List-Objekt. Was hat es damit auf sich?
Tatsächlich zeigt flags auf ein dict-Objekt, das dann erzeugt wird, wenn es ein key-value-pair gibt.

Einträge ins flags-dict passieren über sigale (signals), die ein besonderes Ereignis repräsentieren, welches bei einer Rechenoperation mit einem Dezimalobjekt aufgetreten ist. Zyum Beispiel: ungenau, Rundung.

>>> a = decimal.Decimal(1)
>>> b = decimal.Decimal(3)
>>> a/b
Decimal('0.333')
>>> decimal.getcontext()
Context(prec=3, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=0, clamp=0, flags=[Inexact, Rounded],
traps=[InvalidOperation, DivisionByZero, Overflow])
>>> decimal.getcontext().flags[decimal.Inexact]
True
>>> decimal.getcontext().flags[decimal.Rounded]
True
>>> decimal.getcontext().clear_flags()
>>> decimal.getcontext()
>>> Context(prec=3, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=0, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])
>>>

Nach der Rechnung konnten wir also nachfragen, ob das Ergebnis ungenau ist bzw. ob gerundet wurde. Mithilfe der clear_flags()-Methode kann man die Einträge im flags-dict zurücksetzen, und in der flags-list der getcontext()-Ausgabe sheen wir ein leeres list-Objekt.
Welche Objekte k&oml;nnen nun im flags-dict des Context landen? Die Namen sind im decimal-namespace zu finden

Manchmal möchte man, dass unerlaubte Rechenoperationen (z.B. 1/0) nicht zum Abbruch des Programms führen. Mithilfe von traps kann man Ausnahmen bauen. Ungültige (ausgenommene) Rechenoperationen kUönnen den Wert NaN (Not a Number) liefern.

Die per default gesetzten traps sollen eben diese unmöglichen Operationen ausschließen.
Wollte man diese traps mit einem schlag ausschalten, ginge das folgendermaßen (ExtendedContext ist einer der vordefinierten Context-objekte.

>>> type( decimal.ExtendedContext )
<class 'decimal.Context'>
>>> decimal.setcontext( decimal.ExtendedContext )
>>> decimal.Decimal(1) / decimal.Decimal(0)
Decimal('Infinity')
>>> decimal.Decimal(-1) / decimal.Decimal(0)
Decimal('-Infinity')
>>> decimal.Decimal('-Infinity') * 2
Decimal('-Infinity')
>>> decimal.Decimal('eins')
Decimal('NaN')
>>>

Wenn es um Währungen geht, werden Decimal-Objekte besonders gerne eingesetzt - es gibt aber sicher viele weitere Bereiche, wo große Genauigkeit vonnöten ist.
Im folgenden Beispiel nehmen wir das Euro-Peso (Philippinen) her, und wollen ausrechnen, wieviel etwas auf den Philippinen in Euro kostet.

>>> from decimal import Decimal
>>> einEuro = Decimal('55.33988239')
>>> einCent = Decimal('.00')
>>> hotel = Decimal('7277')#in peso
>>> (hotel/einEuro)
Decimal('131.4964847362047312077780511')
>>> (hotel/einEuro).quantize(einCent)
Decimal('131.50')
>>>

Der Quotient aus hotel/einEuro ergab ja wieder ein Decimal-Objekt - und quantize war eine Methode an diesem Objekt. Easybieeeezy.
Per default erfolgt das Aufrunden hier ab 5.

disassemble

>>> import dis
>>> def f(x):
...   return x
... 
>>> dis.dis(f)
  2           0 LOAD_FAST                0 (x) 
              3 RETURN_VALUE         
>>>

glob

Wie oft haben wir schon sogenannte wildcards eingesetzt? Unzählige Male. Dass die auf der commandline funktionieren hängt davon ab, ob globbing eingeschaltet ist.

Auf der commandline verwendet man, zum Beispiel, folgende Abk¨rzer:

?> ls *.pdf > /dev/null
?> ls [^a]*.pdf > /dev/null
?> ls test[2-5]?[01-9][zb]*.tex

Also, das ist wohl bekannt. In python haben wir dafür das sogenannte glob-Modul. Dieses enthält, zum Beispiel, die glob()-Funktion, die genau einen Parameter entgegennimmt. Dieser Parameter ist entweder
/absolute/Pfad/angabe/*.pdf oder
./relative/Pfadangabe/*.pdf oder
*.pdf, also im aktuellen Verzeichnis.

Sehjen wir uns 'mal erste Beispiele dazu an.

>>> import glob
>>> a = glob.glob('*.pdf')
>>>  print ( type(a) )
<class 'list'>
>>> print ( len(a) )
52
>>> print ( a[3] )
Babylon 5 - Ships Of The Galaxy.pdf
>>> a.sort()
>>> print ( a[3] )
32xX5650+24->48GB.pdf
>>> b = glob.glob('*[el].pdf')
>>> print (b)
['schule.pdf', 'GAMMA_Eulers_Konstante.pdf', 'stl.pdf', 'hashcat_user_manual.pdf']
>>>

heapq

>>> import heapq
>>> len( dir(heapq))
30
>>>

Auch heapq gehörte bei dir aus dem Dornröschenschlaf geküsst. Draufgekommen bin ich über die Funktionen nsmallest()/nlargest().

>>> zahlen = [-2,8,2,-1,0,11]
>>> klein = heapq.nsmallest(2,zahlen)
>>> gross = heapq.nlargest(2,zahlen)
>>> print( klein, gross, )
[-2, -1] [11, 8]
>>> heapq.nsmallest(3,'michael')
['a', 'c', 'e']
>>>

die n-kleinsten und n-größten items einer collection werden so einfach und schnell ermittelt.
Will man aber DAS EINE KLEINSTE oder DAS EINE GRÖßTE, verwendet man nach wie vor min() und max().

Beiden Funktionen kann man auch noch (wie bei sort() ein key-Argument mitgeben. Hier ein Beispiel aus dem python-cookbook (2013), das uns eine Idee vermittelt.

>>> portfolio = [
...  {'name':'PB', 'shares':200, 'price':21.09},
...  {'name':'IBM', 'shares':100, 'price':91.1},
...  {'name':'AAPL', 'shares':50, 'price':543.22},
...  {'name':'HPQ', 'shares':35, 'price':31.75},
...  {'name':'HCO', 'shares':45, 'price':16.35},
...  {'name':'ACME', 'shares':75, 'price':15.65}]
>>> cheap = heapq.nsmallest(3, portfolio, key=lambda s:s['price'])
>>> expensive = heapq.nlargest(3, portfolio, key=lambda s:s['price'])
>>> print( cheap )
[{'name': 'ACME', 'shares': 75, 'price': 15.65}, {'name': 'HCO', 'shares': 45, 'price': 16.35}, {'name': 'PB', 'shares': 200, 'price': 21.09}]
>>> print( expensive )
[{'name': 'AAPL', 'shares': 50, 'price': 543.22}, {'name': 'IBM', 'shares': 100, 'price': 91.1}, {'name': 'HPQ', 'shares': 35, 'price': 31.75}]
>>>

Was wir von sorted() kennen, können wir auch mithilfe der heapq-function heapify erreichen, obwohl heapify() im Gegensatz zu sorted() NUR list-objects entgegennimmt.

>>> zahlen = [1,-8,0,12,-11,19,-3]
>>> heap = zahlen[:]
>>> heapq.heapify(heap)
>>> print(zahlen)
[1, -8, 0, 12, -11, 19, -3]
>>> print(heap)
[-11, -8, -3, 12, 1, 19, 0]
>>> print( type(heap))
<class 'list'>
>>>

heapq ist sehr interessant, was seine Weiterentwicklung angeht, da es neue Apsekte von algorithm/Datastructures mit einbezieht.heappop() gibt das erste Element einer Liste zurück und entfernt es aus der Liste - wurde die Liste vorher mittels heapify() sortiert, dann kommen die items nach ihrer Größe (mit dem Kleinsten beginnend) 'raus.

>>> x = zahlen[:]
>>> heapq.heapify(x)
>>> heapq.heappop(x)
-11
>>> heapq.heappop(x)
-8
>>> x
[-3, 0, 19, 12, 1]
>>>

inspect

Dieses Modul war mir lange unbekannt, bis ich auf getfullargspec() gestoßen bin, mit dessen Hilfe man Argumente einer Funktion näher beleuchten kann.
Und so stieß ich auf den Rest an Funktionen, die in diesem Modul verborgen sind. Einfaches Beispiel:

>>> import inspect
>>> def beispiel(a:int, b=1, *c, d, e=2, **f) -> str:
...     pass
...
>>>

Eine Funktion, die, laut Deklaration, ein str-Objekt retourniert; und es interessiert uns nur die Kopfzeile, daher das so praktische pass; und diese Kopfzeile wollen wir nun analysieren.

>>> inspect.getfullargspec(beispiel)
FullArgSpec(args=['a', 'b'], varargs='c', varkw='f', defaul
ts=(1,), kwonlyargs=['d', 'e'], kwonlydefaults={'e': 2}, an
notations={'a': <class 'int'>, 'return': <class 'str'>})
>>>

Lasst uns anfangen mit jenen Funktionen, die Abfragen, ob ein Objekt dieses oder jenes ist (z.B. isbuiltin()?)
Und von dieser Liste picken wir uns ien paar heraus.

>>> import inspect
>>> for i in dir(inspect):
...     if i.startswith('is'):
...         print (i)
...
isabstract
isbuiltin
isclass
iscode
isdatadescriptor
isframe
isfunction
isgenerator
isgeneratorfunction
isgetsetdescriptor
ismemberdescriptor
ismethod
ismethoddescriptor
ismodule
isroutine
istraceback
>>>

Du siehst schon, da gibt's ein paar, die man immer wieder brauchen kann. Es folgen nun Beispiele,die selbsterklärend sind.
Es liegt in der Natur der Sache, dass diese Funktionen True/False zurückgeben.

>>> inspect.isbuiltin(len)
True
>>> len = 5
>>> inspect.isbuiltin(len)
False
>>> del len
>>> inspect.isclass(int)
True
>>> def example():
>>>     pass
>>> inspect.isfunction(example)
True
>>>

Schauen wir uns 'mal an, welche Funktionen vorhanden sind, die mit get beginnen.

>>> for i in dir(inspect):
...     if i.startswith('get'):
...         print (i)
...
getabsfile
getargs
getargspec
getargvalues
getattr_static
getblock
getcallargs
getclasstree
getcomments
getdoc
getfile
getframeinfo
getfullargspec
getgeneratorstate
getinnerframes
getlineno
getmembers
getmodule
getmoduleinfo
getmodulename
getmro
getouterframes
getsource
getsourcefile
getsourcelines
>>>

Ich werde wieder nur ein paar herausgreifen.

>>> import subprocess
>>> inspect.getabsfile(subprocess.Popen)
'/usr/local/lib/python3.2/subprocess.py'
>>>

keyword

erlaubt einer python-Programmiererin herauszufinden, ob ein string ein keyword ist.

>>> import keyword
>>> print (keyword.__all__)
['iskeyword', 'kwlist']
>>> keyword.kwlist
['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
>>> keyword.iskeyword('print')
False
>>> keyword.iskeyword('which')
False
>>> keyword.iskeyword('while')
True
>>> keyword.iskeyword('if')
True
>>>

fnmatch

ifnmatch nimmt eine shell-wildcard entgegen. Die bekanntesten wildcards sind:
*, ?, [185], auto{mobil, mat, radio}
Es stehen genau vier Funktionen (python-3.3) in der __all__-Liste, nämlich fnmatch(), fnmatchcase(), translate() und filter().

fnmatchcase()

soll wildcards in RE's übersetzen (ohne diese gleich zu kom;pilieren).

>>> fnmatch.translate('auto{mobil, mat, radio, r, start}')
'auto\\{mobil\\,\\ mat\\,\\ radio\\,\\ r\\,\\ start\\}\\Z(?ms)'
>>>

fractions

In der Mathematik hörten wir von der Zahlenmenge der Rationalen Zahlen (Q), die alle Ganzen Zahlen (Z) und somit auch die Natürlichen Zahlen (N) beinhaltet.
Rationale Zahle sind ganze Zahlen mit einem (oder auch keinem) Bruchstück einer Zahl. Easy.

3.1 5 21/2 - all das sind bereits rationale zahlen. Jede Zahl, die sich als Bruch darstellenb lässt gehört zur Menge der Rationalen Zahlen. Daher gehört, zum beispiel, die Zahl Pi nicht dazu, denn Pi ist eine Irrationale Zahl (Lässt sich eben nicht als Bruch darstellen).

Der Modulname fractions triffts auf den Punkt. eine Ganze Zahl PLUS ein Bruchstück (fraction) einer Zahl.

>>> import fractions
>>> dir(fractions)
['Decimal', 'Fraction', '_PyHASH_INF', '_PyHASH_MODULUS', '_RATIONAL_FORMAT', '__all__',
'__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__',
'__spec__', 'gcd', 'math', 'numbers', 'operator', 're', 'sys']
>>> print( fractions.Fraction(1,2) )
1/2
>>> fractions.Fraction(16,-10)
Fraction(-8, 5)

ok - nun eine ganz einfache Rechnung (für'n Anfang):

>>> zahla = fractions.Fraction(16,-10)
>>> zahlb = fractions.Fraction(5,25)
>>> zahlc = zahla + zahlb
>>> print( zahlc )
-7/5
>>>

functools

wraps()

Ist eine decorator-Funktion, die in deco.html gezeigt wird.

webbrowser

Dieses Modul ermöglicht es der Programmiererin, eine Website im Standardbrowser zu öffnen.
Das geht, zum Beispiel, folgendermaßen:

>>> import webbrowser
>>> webbrowser.open("http://www.python.org", 2)

Das öffnete die python.org-Seite im Standadbrowser in einem neuen tab. Die Syntax sieht so aus:

webbrowser.open(url[, new[, autoraise]])

wobei new 0, 1 oder 2 sein kann. Die Zahlen bedeuten:

Übergibt man für den Parameter autoraise True, dann wird das Fenster mit der geöffneten URL in den Vordergrund geholt (das kann aber auch bei manchen Systemen automatisch passieren).

>>> webbrowser.open_new('http://www.python.org')
...

öffnet URL in einem neuen Browser-Fenster und

>>> webbrowser.open_new_tab("http://www.python.org")
...

öffnet URL in einem neuen tab.

getopt

Seit python-3.2 verwende ich argparse, und daher ist auch in der stl argparse als einzigeer Optionen-parser ausreichend gut erklärt und vorgestellt worden.
getop erinnert an das Unix getopt(). Es war sehr beliebt (8nd ist es bei manchen wahrscheinlich immer noch), als es noch kein iptparse und kein argparse gegeben hat.

Wenn du es einsetzen möchtest, dann erhältst du alle Information auf den python-docu-Seiten im Netz oder über die help().

getpass

getuser() nimmt als user den aktuell eingeloggten. Passwort wird im Klartext abfespeichert.

>>> import getpass
>>> user = getpass.getuser()
>>> passwd = getpass.getpass()
Password: 
>>> print( user )
itsme
>>> print( passwd )
nopassapon
>>>

operator

>>> import functools
>>> import operator
>>> functools.reduce(operator.add, [1,2,3,4,5,6,7,8,9])
45
>>>

pprint

pprint steht für pretty print; und Nomen ist Omen - es erleichtert uns die Ausgabe eines Objekts wie etwa einer Liste, Dictionary, etc. schön un schöner zu gestalten.

Das Modul pprint hat zwei elemente, die uns interessieren - die Funktion pprint() itself und die Klasse PrettyPrinter, die wir uns gleich in Aktion anschauen werden.

>>> from pprint import pprint,PrettyPrinter
>>> tiere = {'saeugetiere':['Affe','Mensch','elefant','Wal'],'Fische':['hai','Forelle','karpfen'],'Insekten':['Schmetterling','Hausfiege']}
>>> print (tiere)
{'saeugetiere': ['Affe', 'Mensch', 'elefant', 'Wal'], 'Insekten': ['Schmetterling', 'Hausfiege'], 'Fische': ['hai', 'Forelle', 'karpfen']}
>>> pprint(tiere)
{'Fische': ['hai', 'Forelle', 'karpfen'],
 'Insekten': ['Schmetterling', 'Hausfiege'],
 'saeugetiere': ['Affe', 'Mensch', 'elefant', 'Wal']}
>>> p = PrettyPrinter(indent=4,width=5,depth=2)
>>> p.pprint(tiere)
{   'Fische': [   'hai',
                  'Forelle',
                  'karpfen'],
    'Insekten': [   'Schmetterling',
                    'Hausfiege'],
    'saeugetiere': [   'Affe',
                       'Mensch',
                       'elefant',
                       'Wal']}
>>>

struct

>>> import struct
>>> struct.pack( b'B',130 )
b'\x82'
>>>

zipfile

>>> import zipfile
>>> with zipfile.ZipFile('/home/itsme/Downloads/20150502_Opening_Books.zip','r') as archive:
...     archive.printdir()
... 
File Name                                             Modified             Size
20150502_Opening_Books/                        2015-05-02 21:16:00            0
20150502_Opening_Books/.DS_Store               2015-05-02 19:55:20         6148
__MACOSX/                                      2015-05-02 21:16:12            0
__MACOSX/20150502_Opening_Books/               2015-05-02 21:16:12            0
__MACOSX/20150502_Opening_Books/._.DS_Store    2015-05-02 19:55:20          120
20150502_Opening_Books/a-flank.bin             2015-05-02 15:15:16       286880
__MACOSX/20150502_Opening_Books/._a-flank.bin  2015-05-02 15:15:16          329
20150502_Opening_Books/b-semiopen.bin          2015-05-02 17:03:12       651696
__MACOSX/20150502_Opening_Books/._b-semiopen.bin 2015-05-02 17:03:12          329
20150502_Opening_Books/c-open.bin              2015-05-02 16:58:32       305904
__MACOSX/20150502_Opening_Books/._c-open.bin   2015-05-02 16:58:32          329
20150502_Opening_Books/d-closed.bin            2015-05-02 18:43:44       362032
__MACOSX/20150502_Opening_Books/._d-closed.bin 2015-05-02 18:43:44          329
20150502_Opening_Books/e-indian.bin            2015-05-02 19:21:58       348480
__MACOSX/20150502_Opening_Books/._e-indian.bin 2015-05-02 19:21:58          329
__MACOSX/._20150502_Opening_Books              2015-05-02 21:16:00          210
>>> with zipfile.ZipFile('/home/itsme/Downloads/20150502_Opening_Books.zip','r') as archive:
...     member = archive.infolist()[0]
...     print( member )
... 
<zipfile.ZipInfo object at 0x7f753cf80f48>
>>>

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