Transcript: Das Python Importsystem
Full episode transcript. Timestamps refer to the audio playback.
Ja, hallo liebe Hörerinnen und Hörer, willkommen beim Python-Podcast in der Episode Nummer 33.
Die hatten wir letztes Mal ausgelesen. Deswegen müssen wir die Episode Nummer jetzt ergänzen.
Ja, was machen wir heute? Wir wollen heute über Imports reden. Hi, Jochen.
Ja, li, hallo, willkommen Dominik.
Hi, Johannes.
Ja, hallo, ich bin auch mal wieder da.
Ja, wir kennen uns ja alle schon.
Ja, also über das Python-Importsystem wollen wir heute sprechen.
Vielleicht ein paar kleine News vorweg, wie wir das gerne tun.
Und dann gehen wir so ein bisschen in die Details.
Habt ihr News mitgebracht?
Ja, also die einzigen
News, die ich habe, ist, dass Apple sich gerade den
größten PR-Unfug aller
Zeiten einholt. Aber
da ich kein Apple-Gerät habe, geht es
ein kleines bisschen mehr. Das Problem habe ich auch nicht.
Jochen, erzähl du doch mal.
Ja, nee,
tatsächlich ist es PR-technisch, würde ich
sagen, auch aus meiner Perspektive ein großer Unfall.
Wenn man sich jetzt anguckt, was sie
technisch tun, ist es nicht so furchtbar schlimm.
Was ist denn überhaupt passiert? Vielleicht jetzt mal so die Story.
Sie haben irgendwie
War das eine Pressemeldung?
Es war eine Pressemeldung und es war eine technische Meldung, glaube ich,
dass sie jetzt CSAM
angemacht haben, Contact Content Scanning
and
keine Ahnung, was es bedeutet.
Ja, jedenfalls
wollen sie, jedenfalls will
Apple jetzt in die Fotos reingucken und
verbotene Inhalte finden.
Ja, das ist natürlich
so ein bisschen, das, ja.
Mit modernster Technologie.
Ja, zum Glück
ist es nicht das Schlimme.
und Technologie, was man halt auch nehmen könnte,
sondern es ist halt irgendwas total Billiges,
einfach nur Hashes in Bildern angucken.
Also im Grunde nur gucken, ist das ein
Bild, das auf irgendeiner Liste draufsteht
von verbotenen Bildern.
Ja, aber haben die nicht auch gesagt, dass sie so
neuronale Netze drüberlaufen lassen
und dann Sachen erkennen wollen?
Oder ist das erst der nächste Schritt?
Ja, genau, das ist halt der
Agau-Teil. Ja, genau, und das ist auch so ein bisschen
der Präzedenzfall.
Früher war Apple ja immer so, ja,
es ist auf deinem Gerät und es ist alles sicher und wir können
da gar nicht. Und jetzt ist es so, ja, wir gucken doch mal rein.
Ja, also
entweder war es ein Unfall,
sozusagen, dass jemand nicht drüber nachgedacht hat,
dass, wenn man das so sagt, dass das dann genau
das auslöst bei den Leuten.
Oder es ist halt irgendwie, aber das kann ich
mir auch nicht vorstellen, weil das wäre ja so
ein abrupter Strategiewechsel. Das wäre auch schon
irgendwie komisch. Also insofern nehme ich mal an,
dass es eher ein PR-Unfall ist.
Man weiß es nicht. Aber es ist ein
Präzedenzfall. Apple scannt jetzt Inhalte
auf euren Geräten.
Das machen sie
Das machen sie ja auch sowieso schon immer.
Also man kann ja auch in der Fotos-App
irgendwie nach Hund gucken
oder nach Personen oder sonst irgendwas.
Und da ist tatsächlich, das ist halt die gute Technik
sozusagen, die da verwendet wird.
Und ja, man kann sogar...
Aber auch da sind viele Sachen falsch einsortiert.
Ja, aber sie sind da auch
halbwegs schnell und so. Man kann auch nach
Vaccination oder so suchen und dann findet es den Impfpass,
wenn man den fotografiert hat und so.
Also da machen sie auch wirklich das gute
Zeug.
Und machen das halt
auf dem Gerät selber.
Aber genau, nur halt, wenn man selber sagt, man hätte das gern und ja, und halt, es wird auch nicht an irgendwen geschickt. Und jetzt ist das natürlich eine andere Sache, wenn sie jetzt irgendwas eines von den verbotenen Bildern findet, dann sagt es irgendwie dann Petzl.
Vielleicht verbotene Bilder?
Ja, wer weiß.
Genau, und wir wissen auch, dass es da welche gibt, ja. Also das ist natürlich alles sehr unangenehm und das wäre dann schon ein Präsenzfall, weil das haben sie bisher nicht gemacht.
Na gut, mal sehen, wie sich das entwickelt. PR-mäßig ist es auf jeden Fall ein mittleres Resultat.
und das ist von einem Impfpass gesagt, das heißt, du bist auch jetzt durch.
Ich bin schon lange durch.
Ich auch jetzt seit einer Woche.
Also hier auch nochmal der Aufruf von allen Zuhörern, lasst euch bitte impfen.
Ja, das ist eine gute Idee.
Wie viele haben wir jetzt verloren?
Die Zuhörer.
Es gab so einen tollen Rant-Post von Linus Torwalser.
Ja, der sagt auch, lasst euch impfen, ihr Trottel.
Ja, genau.
auf der Tour.
Ja, Linus Torwalds
benutzt noch mehr ausgewählte Wörter,
um das zu beschreiben, sagen wir es mal so.
Ja, aber inhaltlich
doch. Inhaltlich war es die Zusammenfassung, ja.
Ja, haben wir noch Python-News?
Ja, also eine
News, die so ein bisschen anschließt an
unsere letzte Episode zu Packaging,
ist, dass tatsächlich,
das war vielleicht schon länger klar,
aber mir war es halt noch nicht klar, ab
Python 3.11
wird Distutils
halt deprecated.
Und das heißt,
man muss sich da an der Stelle dann halt mal was überlegen,
was man dann so tut.
Aber es gibt doch so viele Alternativen.
Ja. Diverse Alternativen.
Also die
Pakete, die jetzt halt so
ein richtiges Problemchen bekommen,
sind halt sowas wie
NumPy und SciPy und so, weil
die haben nämlich, die erben nämlich von
Distutils und haben da Extensions für gebaut
und damit bauen sie halt alles.
Ja, okay.
und ja,
also die gucken sich gerade andere Sachen an
und
tatsächlich gibt es da, also sie haben sich zum Beispiel
CMake angeguckt oder halt auch
Mason heißt das irgendwie
und tatsächlich
also ich manchmal, also sogar relativ
häufig, ich habe ja hier so ein M1
Mac auch und da passiert
es durchaus, also wenn die Python-Version hochgeht oder so,
ist es eigentlich immer so, dass wenn ich dann
Projekte habe, die
abhängig haben zu NumPy,
Pandas oder so, dass das Ganze zwar kopiert werden muss,
weil meistens ist das Update von Python halt schneller bei mir, als die Wheels draußen sind.
Und dann muss ich das halt kompilieren und dann denke ich mir so, oh nein, jetzt dauert das wieder ein paar Minuten.
Und tatsächlich dauert das bei NumPy, glaube ich, sieben Minuten irgendwie mit dem klassischen jetzt des Tutels.
Und tatsächlich aber auf Mason basierten, hat hier jemand schon den Namen vergessen, angefangen damit rumzuexperimentieren, dauert es halt irgendwie noch vier Sekunden oder so.
Also es ist hauptsächlich irgendwie
Bauzeit,
die da
benötigt wird.
Also insofern, mal gucken.
Könnte sein, dass es alles schneller wird demnächst.
Das wäre auch ein sehr positiver
Effekt von dieser Deprecation.
Genau. Und ansonsten,
ja, ich glaube 3.11
ist in der Beta.
3.10 ist in der Beta.
Ich glaube, jetzt um den Dreh
ein Rieskandidat.
Genau.
Die Zahlen gehen so schnell hoch.
Ja, ja, ja. Freuen wir uns schon.
Also ich habe mal den GitHub Copilot,
den wir auch schon mal kurz erwähnt hatten, ausprobiert.
Da habe ich jetzt auch eine Beta-Einladung bekommen.
Muss sagen, das ist echt toll.
Also ich habe noch nie so einen
super Effekt von einer KI erlebt.
Ich finde es echt nice. Ich gebe ein, also ich habe
jetzt die VS Code Extension dafür benutzt.
Sowas wie Define and Fibonacci oder sowas.
Und dann warte ich
auf das Autocompletion und dann schreibt er mir die komplette
Funktion inklusive Docstrings und Type Annotations
als Type-Int und ich drücke einmal
Tabulator und es ist fertig. Und das geht
halt auch für Sachen, die ich selber schreibe. Wenn ich jetzt
eine Klasse schreibe, verschiedene Methoden nehme, beispielsweise
irgendwie HTTP-Methode.
Ich habe jetzt irgendwie ein Get geschrieben, ne, in einer Klasse.
Define, Get, bla bla bla. Und danach
fange ich an mit Define und dann steckt er mir
als nächstes vor, Post zu definieren.
Und zwar auch mit genau den richtigen Parametern und so.
Das sind solche Sachen, das ist halt einfach,
der lernt halt aus seinem eigenen Code raus und das ist
wow. Also ich bin wirklich beeindruckt davon,
was das so macht. Also das ist so ein bisschen,
man braucht keine Snippets mehr, ja, wenn man irgendwie
Snippets-Sammlung hat, sondern man nimmt halt einfach dann
das individualisierte Snippet und das ist
und Jochen unterhalten sich über die Programmiersprache Python
Modelle trainiert werden?
Nein.
Das ist halt etwas, was sich allmählich einbürgert.
Der Begriff dafür nennt sich
Self-Supervised Learning.
Und zwar deswegen, weil man halt
zum Beispiel auf Text
relativ gut Modelle trainieren kann,
ohne irgendwelche gelabelten Daten
haben zu müssen. Was man ja normalerweise ist, ist immer das Problem
bei
irgendwie, wenn man so überwachte
Modelle, also
anführungsweise normale Machine Learning Modelle
trainiert, dann braucht man halt Trainingsdaten
und damit meint man üblicherweise halt
Daten, wo man
Labels dran geschrieben hat, wie zum Beispiel
jetzt, wenn man jetzt Bilder hat, so was ist ein Hund, das ist eine Katze
und das ist, weiß ich nicht, irgendwas
dazwischen oder so
und
bei Texten hat man das ja auch,
da gibt es dann ein klassisches Problem wie Textkategorisierung,
da hast du halt einen Text und dann steht halt zum Beispiel so
diese Newsreuters-Artikel, da steht dran,
das ist jetzt Sport oder das ist Politik oder das ist halt irgendwas anderes
und
und... Cool wäre ja so das Label wie, will ich lesen, will ich nicht lesen?
Kann man auch alles machen.
Wenn du da Trainingsdaten hast, dann kannst du Modelle drauf trainieren, die dann auch wahrscheinlich
halbwegs gut funktionieren sollten, also das geht alles. Aber das Problem ist
natürlich, dass für die meisten Probleme, die man so hat, hat man halt eben keine Trainingsdaten und das
Erstellen von Trainingsdaten ist halt so aufwendig, dass sich das dann meistens nicht lohnt, das zu machen.
Wie viel braucht man denn da, ungefähr 100.000? Also kommt drauf an, wie gut es sein soll.
und dann entsprechend
unter Umständen schon Millionen von Beispielen oder so
und das ist dann halt doof, weil das ist einfach zu viel.
Die meisten Leute haben so viele Beispiele gar nicht.
Nee, genau. Also einmal, du hast
gar nicht genug Beispiele, um sie labeln zu können,
dann musst du die erstmal besorgen, dann musst du es auch noch irgendwie labeln
und dann hast du Fehler in den Labels, weil
halt die Leute das nicht perfekt gemacht haben und dann
musst du das irgendwie kontrollieren und
dann schwuppdiwupp hast du irgendwie so ein Redaktionssystem
und eine Redaktion, eine technische Redaktion
und dann Prozesse und
keine Ahnung und Abstimmungen und das kostet
Schweinegeld und das macht dann halt keiner so einfach so.
Sondern nur, wenn man es halt wirklich, wirklich braucht.
Und
ja, aber
man kann halt aus Text auch
lernen, ohne diese ganzen Geschichten
haben zu müssen. Also
es gab ein Paper 2017,
ist das glaube ich irgendwie, Attention is all you need.
Da wurde halt diese Transformer-Architektur
eingeführt und
diese
Dinge funktionieren auf Sprache halt total super.
die funktionieren so,
und Jochen unterhalten sich über die Programmiersprache Python
in die Richtung, dass es das halt beim nächsten Mal besser macht.
Und das machen wir dann ganz oft
und dann kann es halt
aus einem Text, ohne dass man da irgendwelche Labels
explizit für bräuchte oder so,
Dinge lernen, und zwar fast alles.
Und das ist halt das Interessante.
Man kann das gesamte Internet nehmen
und dann lernt es halt
lustigerweise eben nicht nur
irgendwie so stumpf, welches Wort
die höchste Wahrscheinlichkeit hat,
dazustehen, sondern um das gut machen
zu können, lernt es halt auch eine ganze Menge
Konzepte und wie Dinge in Verhältnissen
stehen und solche Sachen. Auch Kontext tatsächlich,
das ist halt spannend. Also zum Beispiel
das Plugin schafft das halt auch,
Kommentare richtig zu schreiben. Das heißt, ich fange
mit einem Kommentar an, zwei Worte, und das macht
einen relativ guten Vorschlag für, was da stehen sollte.
Ja, ja, ja, genau.
Das ist eine
Geschichte, die man halt auch machen kann, dass man sagt,
okay, das ist ein Text, der Rest ist halt Lücke,
dann füll mal. Und dann kommen da auch
schon ganz interessante Geschichten bei raus.
Also wenn man sich diese generierten Texte
anguckt, dann ist halt schon immer so, dass man
irgendwann wird so leicht absurd oder
sehr verdreht irgendwie und
merkt so, okay, so ganz, schon so ein bisschen
neben der Spur. Ja, man kann es halt schnell kaputt machen.
Ja, die nähern dann auch so ein bisschen.
Wenn man halt so ein bisschen nicht falsch ist.
Kann man es schnell kaputt machen,
dann kann es immer so. Ja, ich habe noch
was dazu gelesen,
was in eine andere Richtung geht, dass es jetzt
wohl eine große Anzahl inzwischen von
wissenschaftlichen Veröffentlichungen gibt,
denen man durch Analyse
ansehen kann, dass eben nicht von
im Menschen geschrieben würden, sondern von einem neuronalen Netzwerk.
Und die Technik,
die die da verwendet haben, die ist total witzig,
weil diese Netzwerke für
so Paper-Generierung
und für Text-Generierung generell,
die werden wohl bestraft, wenn sie Plagiate
erzeugen. Weil das willst du natürlich nicht.
Du willst natürlich nicht ein Paper einreichen, das dann als Plagiate
abgerichtet wird. Und deshalb
wählen die sehr viele Synonyme.
Die sprechen sehr viel in Synonymen.
Und auch eben so Fachbegriffe
werden dann oft in Synonymen
genommen. Das nette Beispiel,
was ich erkenne, war eben, dass da die Paper
über Big Data, die haben die ganze Zeit über
Colossal Information gesprochen.
Was eben kein normaler Mensch
schreiben würde. Und dann haben sie eben gesucht, wie oft
dieser Begriff
Colossal Information in den Papern
vorkommt und die dann eben genauer
angesehen und die hatten eben
alle diese Anzeichen von erzeugten Papern.
Und das ist so ein bisschen jetzt quasi
die Kehrseite von Copilot, dass die eben nicht
nur, man muss es dann trotzdem noch für was
Gutes einsetzen und nicht für
Ja, du bist ja schon
da, du brauchst Pages beizutreiben.
Ja, aber
genau, was ich
noch erwähnen wollte, war,
genau, man kann jetzt halt sozusagen Modelle trainieren
auf allen Texten, die es im Internet gibt
und die lernen halt tatsächlich da auch
Dinge dann raus und das ist natürlich total super und das geht
bei Sprache vor allen Dingen deswegen, weil
man halt eine endliche
Anzahl von möglichen
Füllern
sozusagen für die Lücken. Was? Noam Chomsky
würde dir widersprechen, Jochen?
Ja, ja gut. Aber man kann sich da ja beschränken.
Man kann ja sagen, okay, meine Sprache hat nur
20.000, 30.000
Vokabeln
und das war's. So größer ist mein Vocabulary
einfach nicht. Ja, okay. Und wenn du immer nur ein Wort
rausnimmst, ist natürlich klar. Und unter denen
wird auch eins sein, das hinreichend nah dran ist.
Und dann kannst du eben allen Worten
eine Wahrscheinlichkeit zuweisen. Du kannst halt sagen,
okay, das ist jetzt sehr, sehr unwahrscheinlich
und das ist jetzt irgendwie, dass das geht.
Das sind alle sehr unwahrscheinlich.
Es gibt dann bestimmt das Wort wie F, das was überall hinpasst.
Ja klar.
Aber das Modell,
das dann halt auch, das Modell, das
damit trainiert wird, passt dann aber nicht mehr überall hin, wenn du das
als akzeptiert hast.
Kennen wir leider.
Jedenfalls, genau.
Also mit Sprache geht das
total super und eben
Copilot ist halt dann
quasi auch eine Folge. Das Tolle ist, dass man
jetzt. Modelle, die da drauf trainiert sind,
kann man jetzt mit ganz wenig Trainingsbeispielen
auf das
Problem, das man wirklich hat,
adaptieren. Also da braucht man dann nur noch
ein paar Trainingsbeispiele, also
nicht mehr viele. Ja, ich glaube auch,
er macht das tatsächlich anhand des Repos,
was du gerade auffasst. Kann er schon gucken
und kann da schon dann die Completion zum Beispiel oder die
Vorschläge anpassen auf das, was du da gerade machen
willst. Und das ist schon echt wow.
Ja, und das ist halt natürlich total toll, weil
das dann halt läuft,
dass du im Grunde dieses Trainingsproblem
Trainingsdatenproblem loswirst.
Und ja,
eine ganze Menge Anwendungsfälle
plötzlich von, ist zu aufwendig,
unlösbar, lohnt sich nicht, in Richtung
kann man schon mal probieren,
wandern und dann ist es natürlich toll.
Und ja, also da wird es
eine Menge interessante Anwendungen geben.
Also gerade für die ganzen V-Programmierer unter uns, also das ganze
Zeug drumherum, das geht halt einfach jetzt viel
automatisierter.
Ja, Kinderregegen, das ist schlecht für die
Auftragsprogrammierer.
Das finde ich nicht gut.
Das kann schon sein.
Ja.
Aber genau, das Problem
ist aber jetzt, dass man das nicht
machen kann für, also was halt, also
man würde, wäre sehr viel weiter,
wenn man das jetzt auch für zum Beispiel
Bilddaten machen könnte. Also und
tatsächlich ist es wohl so, dass es da
gibt es auch inzwischen Forschung zu,
das ist halt tatsächlich der Weg, also man hat sich ja das
oft lange, oder ich erinnere mich noch dran,
dass es mal völlig unklar war, wie das
eigentlich funktioniert, wie lernen Kinder eigentlich
sozusagen so schnell Dinge, man versteht es nicht, sie sehen eigentlich zu wenig Trainingsbeispiele
für das, was sie da lernen und tatsächlich lernen sie ja auch so etwas, was einem die
Forschung sagt, manchmal Schwierigkeiten hat, das einfach so zu akzeptieren, aber die sagt
halt, das meiste, was Menschen so lernen, lernen sie halt so in den ersten neun Monaten
und alles, was danach kommt, ist halt so, naja Gott, also ich meine so Details, also
Die wesentlichen Dinge sind bis dahin gelernt.
Wie machen die das denn?
Kinder in dem Alter verstehen einfach gar nichts.
Die kennen auch keine Worte für nichts.
Wieso können die denn so viel lernen?
Wie soll das gehen? Das ist irgendwie alles seltsam.
Und tatsächlich ist es wohl so,
dass die halt auch schon eine Art
Self-Supervised Learning machen
und quasi genau das gleiche Prinzip irgendwie
sehen halt was
und Dinge passieren
und dann betrachten sie das
sozusagen als eine Art Lückentext und füllen das dann halt
und das funktioniert.
Genau, das ist das Ego, was das dann macht
Na, keine Ahnung
Ja, aber das zieht das dann irgendwo aus dem Äther, irgendwelche
Formationen schafft das dann natürlich und deswegen hast du ja auch
so, weiß nicht, ob ihr das kennt, wenn ihr irgendwo
müde seid oder sowas, dann werden bestimmte Sachen
ja auch einfach ersetzt, der Kopf macht das ja sehr gerne
dass er bestimmte Flecken, die
nicht so ganz klar sind, dann mit Sachen überschreift
also einige Leute, die haben dann auch
innere Stimme, die dann auf einmal das erste Mal vollständig
die nur so halb gehört haben oder
das kannst du auch mit optischen Informationen machen oder mit
anderen optischen Signalen
und das ist halt super spannend tatsächlich
und wenn das halt self-supervised learning ist,
dann ist ja auch klar, dann hast du so ein Lückenbild.
Das ist so ein bisschen so, du hast ein Foto,
da fehlt ein Teil und dann wird das automatisch ersetzt.
Das ist spannend, wenn die KI das kann, dann kann man
quasi, machst du ein Hochzeitsbild von dir, dann kannst du irgendwie
deinen Partner rausschneiden, dann kannst du den idealen Partner auf einmal sehen.
Ah ja, und
so ist also dieses Python
Import-System entstanden.
Das ist ja spannend.
Wie, willst du jetzt zum Topic zurück?
Nein, nein. Nur ganz kurz, also
das, was halt momentan nicht geht, was man nicht
hinkriegt und was halt total toll wäre, wenn man es irgendwie hinkriegen würde,
also vielleicht hat ja irgendjemand eine Idee
das große ungelöste Problem an der Stelle ist halt
wenn du jetzt ein Bild aus einem
Teil aus einem Bild rausnimmst und sagst so
das ist jetzt leer, also einmal
man kann halt irgendwie die
Menge an Bildern, die halt an der Stelle möglich wären
nicht aufzählen, nicht so richtig
und dann ist es so
selbst wenn man sich irgendwie beschränkt
weiß man nicht, so jetzt hat man
zwei Sachen, die eigentlich offensichtlich nichts damit zu tun haben
mit dem Bild, das man eigentlich an der
Stelle gerne hätte, welche Wahrscheinlichkeit weiß man
hinzu und wie ist der Gradient in welche Richtung.
Aber vielleicht braucht man da den Kontext für, weil wenn du einen Kontext
hast, in dem das drumherum gestellt werden soll,
der halt selber ein Objekt ist,
das viele verschiedene Informationen beinhaltet, dann ist es
besser, diese Menge an
abzählbaren Elementen zu bilden und das entsprechend
herauszusuchen mit der höchsten Wahrscheinlichkeit.
Das heißt also, ohne Kontext ist das wahrscheinlich nicht so
einfach möglich, dass du einfach so ein Bild machst, das dann gut trifft.
Aber wenn du Informationen über den Kontext hast,
in dem du dieses Bild ersetzen möchtest...
Also der Punkt ist eher, das ist ja möglich.
Also du kannst ja durchaus ein Modell trainieren,
was dann irgendwas vorhersagt an der Stelle.
Die Frage ist nur, welchen Wert hat das, was es jetzt vorhergesagt hat?
Hat das jetzt die Wahrscheinlichkeit 0,376 oder 0,652?
Und das macht ja einen Unterschied fürs Training, aber es ist halt völlig unklar, wie man da die Wahrscheinlichkeit ausrechnen soll.
Ja, vielleicht braucht man dafür echt einen Kontext, in der es eingesetzt werden soll.
Das ist, glaube ich, gar nicht so.
Ja, also das ist auf jeden Fall, wenn da jemand eine Idee hat, voll gut.
Aber das war jetzt auch gerade wieder so ein Ding, was halt passiert, wenn man halt den Kontext nicht hat.
dann passiert sowas wie ein Relative Import
Beyond Top Level Package oder so.
Oh, das ist ein Syntaxfehler quasi.
Ich sehe schon, es gibt eine gewisse
Abschüssigkeit in eine
bestimmte Richtung. Ja, Importsystem ist auch
voll interessant. Na gut.
Ja, natürlich, das war gerade ein
Versuch, da so ein bisschen hinzukommen, weil wir wollten ja über das
Importsystem tatsächlich reden und
wie man das in Python macht.
Und ich glaube, als Basis irgendwie das so ein bisschen zu verstehen,
es gibt einen super tollen Blog, den ich auch erst letztens
entdeckt habe, der 10.000 Meters heißt,
der irgendwie auf Hacker News nochmal gefeatured worden ist und da hat ein
Mensch namens Viktor Skvorstov, wenn ich ihn richtig ausspreche,
einen Artikel geschrieben, wie das Python-Import-System funktioniert.
Und das hat er so ein bisschen erklärt, das ist sehr spannend zu lesen.
Fangen wir auf jeden Fall in die Shownotes. Und ja, wie funktioniert denn das eigentlich?
Was ist denn das Python Ja ich habe diesen Artikel auf Hacker News gesehen und auch die Diskussion dann so ein bisschen angeguckt und da ist mir aufgefallen dass das doch tats erstaunlich
viel komplizierter ist, als
man das so denkt oder als man das im Kopf hat.
Was mir gar nicht so aufgefallen
war, weil ich offenbar
nicht alle Features benutze, die es da so gibt.
Vielleicht ganz kurz, bevor wir darauf einsteigen,
was ist denn überhaupt das Problem damit?
Wir haben so eine Story gehört, dass das mal lange
macht, was macht denn das?
Worum geht es denn da überhaupt?
Und warum braucht Python ein Import-System überhaupt?
Das ist ja auch eine spannende Sache, weil in anderen Sprachen ist das ja anders
gelöst und zum Teil besser und zum Teil
schlechter.
Wer will
anfangen?
Ich würde sagen, vielleicht kann man genauso anfangen wie der Artikel.
Was passiert denn, wenn man sagt Import M?
Warum sagt man das denn überhaupt,
Jochen?
Es gibt
natürlich Build-Ins, aber nicht alles sind
Build-ins und manchmal möchte man halt
irgendwo Funktionen oder Klassen oder sonst
irgendwas verwenden.
Oder man möchte einfach seinen Code trennen
und nicht alles in eine einzige Pfeile.
Man möchte halt irgendwas verwenden, was nicht in der aktuellen Datei definiert ist.
Genau.
Und jetzt könnte ich verschiedene Dinge,
ich könnte mir die Datei nehmen und sie
e-ballen, könnte ich machen.
Ja.
Wäre
auch möglich, oder?
Wäre auch möglich.
Hat auch einen ähnlichen Effekt irgendwie.
tatsächlich. Aber das
überschreibt dann natürlich manchmal Sachen, wenn ich jetzt irgendwie
zum Beispiel da drin halt,
wenn ich jetzt im aktuellen... Ja, vor allem, wenn man das rekursiv
macht, weil das müsste dann auch immer weiter funktionieren.
Ja. Und dann
wenn man so Counter-Variablen hat oder so,
die sind dann wahrscheinlich hinterher anders.
Vielleicht, ja. Vielleicht. Ja, oder auch
Namen, die einfach gängig sind,
werden vielleicht eventuell überschrieben.
Ja, aber das ist ja, bei Python macht der auch
irgendwie sowas, ne? Das macht ja so ein
Namespace wieder. Ja, genau. Aber das müsstest du dir
dann, also wenn du einfach die Datei lädst
und ausführst, also
e-Val drauf machst, dann
ja, kriegst du halt erstmal alles in die Hand.
Und da wäre dann, hätte man
dann auch wieder das Problem mit dieser Dekursion, die müssen wir auch irgendwie
lösen, weil du musst ja dann eventuell noch mehr Sachen
derweil
importieren und e-Valen und
vielleicht hast du ja sogar das Problem, dass
du es zyklisch machst.
Aber das Problem haben wir in Python auch.
Das Problem haben wir in Python auch, ja, aber
müssen wir mal überlegen, wie das das macht.
Aber in anderen
Sprachen, um da noch kurz drauf anzugehen,
braucht man es ja, macht man es ja
nicht. In Java oder in C++
macht man es ja nicht, dass man einfach
andere Dateien da reinkopiert,
weil da ja
die Organisation anders ist. Da braucht man ja quasi
keine Objekte in der Hand, sondern nur
einen Verweis auf die entsprechenden Stellen
im Code. Das heißt, bei denen
funktioniert das Import-System halt,
weil das während des Kompilierens passiert,
deutlich anders
als in Python.
Ja, ich überlege gerade, wo da die Unterschiede sind.
Also wenn ich in C sage include,
das wäre das eine Datei.
Genau, aber wenn du zum Beispiel in C
includeierst, die sind ja normalerweise nur .h-Dateien.
Und da sind ja keine Definitionen drin,
da sind ja nur Deklarationen drin.
Da steht nur drin, es gibt etwas, was heißt so und so.
Und das ist das Einzige, was du an der Stelle da reinkriegst.
Das Zusammenführen, was dann tatsächlich der Code ist
oder was diese Werte sind. Das passiert erst später
im Link-Vorgang, wenn
du schon alles kompiliert hast. Und diese
Phasen gibt es bei Python ja einfach überhaupt
nicht. Bei Python gibt es ja nur die Phase
Ausführen. Und zu dem Zeitpunkt
brauchst du es dann jetzt. Und
was viele Leute vergessen,
wenn du define schreibst, wenn du eine Funktion definierst,
wenn du eine
Funktion erstellst, dann ist
das Code, der ausgeführt wird, das ist ein Befehl
an Python und der heißt,
merkt ihr mal unter dem Namen, keine Ahnung,
fun, ein Code,
Objekt, was folgende Eigenschaften hat.
Es ist callable und dieser callable hat
folgende Parameter und dann steht da
Code drin und so weiter und so fort. Und auch
bei Class, wenn du Class
als Schlüsselwort, wird ja erstmal
alles ausgeführt, was da drin ist. Das heißt, du kannst da
prinzipiell Code drin haben, der
Sachen tut, was ja auch an vielen Stellen
sehr wichtig ist, was du aber in anderen
Systemen nicht hast.
In der Java und in der C++
hast du das nicht, das wird nicht ausgeführt.
Es wird zum Teil ausgeführt
bei C, weil du da diesen komischen Preprocessor
hast und weil der auch so
constant optimizations
versucht schon mal zu machen,
aber generell wird
C-Code erst ausgeführt, wenn du das Programm startest.
Das ist bei Python,
ja, also diesen Schritt des Kompilierens gibt es ja bei Python
nicht, der ist ja da so ein bisschen innen drin.
Das heißt, du kannst auch mit Import
kannst du auch Sachen ausführen.
Was
manchmal komisch ist und manchmal gewünscht
und manchmal überraschend.
Ja, also das ist manchmal,
also tatsächlich, wenn Sachen importiert
und das dann halt alles in dem Modul ausgeführt wird,
was auf dem Top-Level ist, das ist halt schon
manchmal eher überraschend.
Warum passiert das denn auch?
Warum wird das dann ausgeführt? Also wenn man jetzt
einen Import hat, Import M
von irgendwo, was dann...
Einfach nur Import M, also wirklich nur
diese Zeile Import M. Was passiert dann?
Ja, das ist eine gute Frage.
Und auf den ersten Blick ist es
finde ich so ein bisschen offensichtlich, was da passiert
und auf den zweiten Blick ist es dann tatsächlich doch nicht so offensichtlich,
wie eben dieser Artikel,
glaube ich, uns allen demonstriert hat.
Also für mich bedeutet Import M
suche eine Datei,
die M.py heißt
und gib mir den Inhalt
davon als Modul. Oh, das wäre aber ein Skript.
Und wir könnten auch ein Modul laden.
Wenn ein Modul heißt, das M heißt,
das ist ein Verzeichnis oder sowas.
Aber ein Verzeichnis ist ja
auch nur so eine Art Datei.
Ein Verzeichnis ist ja auch nur ein
Verweis auf die Init.py.
Es gibt Module und Packages
und was ist denn überhaupt der Unterschied?
was ein Skript, was ein Modul, was ein Package.
Ja, das ist schwer zu sagen.
Ich finde es schwer zu sagen, weil
für mich dieser Unterschied wirklich minimal
ist. Das eine ist halt
eine Datei und das andere ist ein Verzeichnis, aber das Verzeichnis
selber hat ja keinen Inhalt
und deshalb tut man so, als ob die InitPy,
die da drin ist, dass der Inhalt dieses
Verzeichnisses ist.
Genau, in der InitPy kann man auch
sowas wie all dann definieren, das heißt, wenn man zum Beispiel sowas wie
import as quick, dann kannst du auch eine einzelne
Datei machen. Kannst du überall definieren.
Ja, okay.
Kannst du auch Slots definieren, das ist auch, kannst du auch überall machen.
Ein Slot? Ja, das sind solche Sachen, die beim Import mitgebracht werden sollen.
Genauso wie All, wenn du All definierst und dann machst du From M Import Stern, dann kriegst du alles das, was in All drin steht.
Also Unterstrich, Unterstrich, All, Unterstrich, Unterstrich, um es jetzt mal hier korrekt zu sagen.
Dann, dann, All, dann, dann.
Das ist ein sehr guter Mechanismus, den sieht man sehr selten, weil Importsternen ja so verpönt ist, so gerne gesehen wird. Das hatten wir schon in der ersten Vorlesung quasi in Informatik 1, hat uns unser Professor schon gesagt, ja man kann using namespace standard nehmen und dann kann man mal gucken, wie viele Symbole da importiert werden und es waren irgendwie 13.000 oder sowas.
und das kannst du in Python natürlich auch machen,
kannst auch from Stern import Stern
probieren,
kriegst halt auch 100.000 Sachen rein.
Deshalb ist
import Stern,
nee, nee, from Stern import Stern geht nicht,
aber so quasi, wenn du es dir
vorstellst. Import Stern heißt halt,
hol alles aus diesem Modul oder aus dem Package
und gib es mir
und meistens weiß man ja gar nicht
ganz genau, was da drin ist. Meistens weiß man auch nicht,
wie viel da drin ist.
Und ganz oft sind da ja dann
auch lokale Sachen drin, die man eigentlich gar nicht haben
möchte. Oder Imports, die
dieses Modul macht,
die hast du dann auf einmal auch selber
importiert. Subdependencies quasi.
Genau, also wenn du in
der m.py, so wie wir es jetzt eben
drin hatten,
wenn du da import, keine Ahnung, import
x machst, und dann machst du from m
import Stern, hast du auch x importiert.
Weil das halt in dem
Modul m dann schon drin ist. Und das ist genau
der Grund, warum man dieses Stern nicht machen soll, weil man nämlich
nicht weiß, was da drin ist und man halt dann alle...
schon gab, bei einem Lokal irgendwo
überschreiben würde. Und wenn mehrere
Module oder Pakete
dasselbe Namen definieren
für irgendwas, was ja durchaus mal vorkommen kann,
dann weiß man nicht mehr genau, was unter dem Namen jeweils
Identität ist. Aber jetzt müssen wir ja erstmal klären,
was Import überhaupt macht. Wir sprechen
jetzt schon die ganze Zeit so, aber wir wissen es immer noch nicht.
Wir wissen aber immer noch nicht genau, was ein Modul, ein Paket ist.
Ja, also ich glaube,
das eine ist eine Datei und das andere ist
ein Verzeichnis.
Es gibt eine Datei.
Ich glaube, es ist auch irgendwie...
der Datei.
Ich glaube, der entscheidende Unterschied ist, dass
ein Paket Submodule haben kann
und ein Modul nicht.
Also genau, ich habe
eine Definition gefunden,
der irgendwo sagt hat, if a module name
has no dots, it's not considered to be part
of a package.
Aha.
Ja, okay.
Also ein Paket ist
quasi eine Sammlung von Modulen oder sowas?
Ja, okay, gut.
Ja.
Da gab es auch eine tolle Antwort drin, Relative Imports for the Billions Times oder sowas.
Ja, relative Imports, darüber muss man auch noch sprechen. Ich bin ein großer Fan davon, aber offensichtlich nicht so. Gut, da kommen wir vielleicht nach.
Ja, ja, ja, okay.
Also was macht denn Import jetzt überhaupt? Import macht, soll ich mal versuchen, soll ich mal mein Verständnis erklären und er korrigiert mich dann.
Von wo importiert ihr denn überhaupt?
Genau, also Import. Erstmal sucht Import ein, also wenn ich sage Import M, dann sucht das Import-System von Python nach etwas, das M heißt. Und zwar auf dem Python Path.
Auf dem Python Path. Ah.
eine Variable, die heißt
sys.pythonpath
sys.path heißt die, oder?
Ich weiß, ich vergesse es jetzt mal wieder.
Jedenfalls ist da eine Menge
von Verzeichnissen drin und
in der Reihenfolge, wie sie in diesem
Path stehen, wird nach
dem Namen
M gesucht. Also 0 ist glaube ich der aktuelle
irgendwie, dass das
entweder vorne oder hinten, das ist immer
Ich glaube 0, der erste ist immer der, wo du gerade bist.
Punkt. Genau.
also die Datei, wo du gerade aufgeführt bist
oder sowas und dann ist halt quasi, der sucht halt dann
im ersten in 1, wo und ab da
und der erste Hit wird dann genommen
Genau, der erste Hit wird dann genommen und in diesem Path
können ganz viele verschiedene Sachen
drin sein, also die Installation
des Installationsverzeichnisses des Interpreters
und wenn man irgendwelche
Environments aktiviert hat, dann sind die da alle drin
man kann da auch selber Sachen reintun
braucht man manchmal, ist manchmal ganz nützlich
Also wenn man verschiedene Pakete
haben will, die auch importiert werden können
dann muss man einfach da gucken, dass
in this.path quasi in der Umgebungsvariante
Das ist im Wesentlichen das, was
VirtualEnv macht. Er macht ein Verzeichnis, wo
du deine Pakete reinlegen kannst und tut den
in den Python-Path für dich. Genau, das heißt, wichtig,
dass euer Path vernünftig konfiguriert ist, wenn ihr Python nutzt,
weil wenn die falsche Reihenfolge drin ist, dann kann
das sein, dass er im Path von Python
2 sucht oder so, also wie es
früher offenbar war. Und dann habt ihr ein Problem.
Genau.
Also, und er sucht diese ganzen Verzeichnisse durch
und wenn da irgendwas drin ist, was diesem Namen m entspricht,
also eine m.py oder ein Verzeichnis, was m
heißt, was eine InitPy hat, oder
in Python 3 ein Verzeichnis, was
das M heißt und Python-Dateien
enthält, was keine InitPy hat,
dann wird
das als Modul importiert
und in den aktuellen Namespace
als Modul M übergeben.
Wir haben gerade wirklich das schon in Python 2 und 3 diskriminiert,
das ist ja interessant. Ja, da gibt es Unterschiede.
Ja, da gibt es auch interessante,
also nochmal ein sehr interessanter Unterschied
kommt mit Python 3.3
glaube ich dazu, nämlich die Namespace-Only-Pakete.
Erklär mal.
Die gibt es.
Früher musste man ja immer die InitPUI haben.
Vor allen Dingen deswegen,
damit halt nicht Verzeichnisse,
die halt so heißen wie ein Standardmodul,
das Standardmodul überschreiben.
Und man halt damit dann
quasi öffentlich
sich dazu bekennt, dass das jetzt ein
Paket sein soll.
Also sozusagen, wenn man sich den Infus
schließt, dann muss man das auch wirklich absichtlich gemacht haben.
Und das ist aber
teilweise blöd.
weil oft will man vielleicht auch
Dinge, die in einem Paket liegen, nicht in einem
Verzeichnis haben zum Beispiel.
Und also wenn es
ein InitPy gibt, dann
ist das
ja dann, jetzt weiß ich
nicht mehr die Stelle, wo das irgendwo drin steht,
dass da drin gesucht werden soll, also
Dispass, ja, aber es gibt auch noch irgendwie
andere Stellen, glaube ich. Es gibt auch noch Module,
die nicht da drin sind, zum Beispiel die
Standardbibliotheksmodule
sind, also es gibt welche, die sind irgendwie in
das Python-Binary reinkompiliert und dann gibt es
auch noch welche, die sind irgendwie
woanders hingemarschelt, aber die liegen da auch irgendwo rum.
Also die Python-Module,
die nicht zählen.
Das stimmt, das kann man, aber es gibt auch noch
Dinge, Module, die sind halt einfach nur
in der Shared Library
und nicht irgendwie Python.
Aber da kommen wir gleich noch so,
wo der Pycache und so weiter noch was, aber egal.
Genau.
Aber man kann halt auch,
es gibt auch Verzeichnisse und denen wird
auch gesucht, wenn da kein NPY ist,
das ist allerdings dann irgendwie
weiter hinten.
Und alle, die den gleichen Namen haben,
wenn da jetzt Sachen drin liegen, dann wird das
zum gleichen Paket
gehörig irgendwie aufgefasst
und das ist dann so ein Namespace-Package.
So ein Namespace-Mengeling.
Und dann kannst du halt auch...
Also wir halten fest, einfach immer eine Init-Pi.
Ja, dann...
Aber so kannst du
halt auch Dinge in einem Paket haben, die in unterschiedlichen
Verzeichnissen liegen, was halt manchmal auch ganz praktisch ist.
Klar.
Ja, okay.
Okay.
Was ist dann, das haben wir gesagt,
Package, es gibt auch ein dann dann Package.
Was steht denn da?
Ist das Current Package?
Genau.
Das ist das aktuelle Paket.
Das ist ein Top-Level-Modul oder sowas, ja?
Ja, oh Gott, ich weiß auch nicht.
Ich glaube, wenn es None ist, dann ist es ein Skript.
Also vielleicht noch mal zu distinguieren,
ist das ein Modul, ein Paket oder so?
Wenn Package None ist, dann ist das nur ein Skript.
Und wenn er sucht, dann auch, wer so ein Package findet.
Und wenn er eins findet, dann ist das Top-Level-Modul,
von dem man dann importieren kann oder so.
Und wenn es das nicht gibt, dann gibt es halt auch
Fehler. Attempted Relative Import
Beyond Top Level Package.
Ja, das ist
so eine Sache, die gibt es.
Passiert mir auch
regelmäßig. Echt, das passiert mir nie, das ist
voll seltsam. Doch, passiert mir auch. Es gibt so Sachen, die
machen Menschen offensichtlich
unterschiedlich und das ist so eine Sache, die mache ich offenbar nicht.
Also nicht, weil ich besser bin oder
weil ich irgendwie das toller kann oder so, sondern
weil es einfach was ist, was ich nicht benutze.
Ja gut, aber wenn du relative Importe benutzt, wie du gesagt hast.
Die liebe ich. Relative Importe sind voll gut.
Davon vertippt man sich ja mal.
Also relative Importe, jetzt müssen wir über relative Importe sprechen.
In Python 3
und ich glaube in 2.7 wurde das dann
irgendwann geportet, gibt es die Möglichkeit zu sagen
from .import irgendwas.
Also from .import m.
Oder vom .dot.
Oder auch from .m
import irgendwas.
Und dieser Punkt heißt halt eben immer
aktuelles Verzeichnis.
also aktueller
Pfad.
Das heißt, wenn es in dieser
Installation, die ich habe, zwei Module gibt, die
M heißen, dann kann ich durch
diesen relativen Import genauer sagen,
welches ich meine.
Ach, okay, doch, nee, das war der Unterschied zwischen Modulen
und Packages.
Module haben einen Pfad dran.
Ich glaube tatsächlich, das ist der entscheidende Unterschied.
Ich glaube schon, ja.
Genau, die Top-Level-Package
ist halt irgendwie doch die Sache.
Also wenn man ein Paket finden kann,
also ein Top-Level finden kann mit diesem Dot-Dot,
dann geht das halt. Aber wenn das None ist,
weil man quasi auf der höchsten Ebene ist, dann gibt es halt Fehler zurück.
Und das ist ja genau der Grund, warum...
Das kann natürlich sein, dass ich das einfach nie...
Also für mich ist einfach Punkt, ja.
Wenn ich sage from Punkt importen, dann weiß ich,
das muss im aktuellen Verzeichnis liegen.
Wenn ich sage from Punkt Punkt, dann weiß ich,
das muss im Verzeichnis drüber liegen.
Und für mich ist das einfach eine Möglichkeit,
quasi in meinem Projekt zu navigieren.
Also ich sag mal so, das geht aber nur dann,
wenn der Interpreter richtig aufgelöst ist,
weil wenn du tief drin bist und dann das Skript
beispielsweise manuell ausführt,
relativ tief drin, dann wird der ja diesen Fehler schmeißen.
Wenn du diese Datei direkt
aufrufst. Das kann man übrigens verhindern,
indem man einfach sagt, Python-m
und dann dieses Modul dann innerhalb
von dem Pfad aufrufen.
Die meisten meiner Projekte sind ja bei einem Django-Umfeld.
Und da bin ich nie in der Verlegenheit,
diese Sachen direkt aufzurufen, sondern die werden dann
entweder über einen Run-Server
aufgerufen oder über einen Test oder
über einen Command.
Auch da ist der Pfad, musst du ja
richtig definieren. Ja, aber der Pfad ist
immer festgemacht an der Managed-Py.
Genau. Das heißt, das weiß ich.
Ich weiß, wo der Pfad...
Ja, aber das ist ja auch Python-USM-Package.module oder so.
Genau. Und deshalb ist das vielleicht
was, was einfach in meiner täglichen
Arbeit nicht so auftritt, dass ich da
mich quasi vernavigiere,
weil meine Projekte eben immer
eine feste Wurzel
haben und darunter kann ich
mich bewegen, wie ich möchte.
Ja, mir passiert das halt vor allen Dingen dann,
also einmal, in Paketen
habe ich oft gerne Tests und in anderen
Verzeichnis. Also da gibt es halt Test nicht innerhalb von dem Projekt selber,
sondern liegen halt in einem Test Directory. Wenn da irgendwas nicht richtig konfiguriert ist, dann passiert das halt
schon mal. Und was mir halt auch ab und zu passiert ist,
in Jupyter Notebooks, weil die liegen halt auch wieder in einem anderen Verzeichnis.
Und die sind halt auch irgendwo. Genau. Und deswegen sehe ich das halt
häufiger mal. Bei diesem Import auch ganz interessant. Ich glaube, das kommt dann wirklich darauf an, wenn der Name
des Moduls Punkte enthält oder halt nicht.
weil wenn er Punkte hat, dann
Achso, wenn der Name des Moduls, also wenn das
Modul quasi m.x.y
Genau, dann ist es halt
ein paar Reihenverkehrs
und wenn es halt
wenn es halt keinen Punkt mehr hat
dann muss es eigentlich schon
Top-Level sein, weil dann kannst du nicht
Aber man kann ja jetzt auch so Dinge tun wie
man sagt
from m import
x oder so
und was mit Wings dann. Habt ihr schon mal Skriptnamen mit
Punkten verwendet? Also mit mehreren Punkten drin?
Nee, das hab ich gerade jetzt überlegt, ob ich
eine Datei mit
Punkten drin hätte, ohne also Punkt
Py. Ich glaube, dass das
zu Problemen führt, oder? Ich glaube auch, dass das zu Problemen
führt und das würde ich auch vermeiden einfach.
Ja, interessant.
Stimmt, das hab ich auch noch nicht.
Das müssen wir, das wird dann die nächste
Episode. Was passiert eigentlich, wenn wir Punkt 1
geben? Ja, genau.
Achso, genau, die Form
import m ist ja
noch vergleichsweise offensichtlich. Aber jetzt hat der Jochen eben gesagt,
was ist denn, wenn ich sage, from m import x?
Genau.
Und was dann halt für mich passiert ist,
er importiert erst m
und guckt dann
in m nach, ob es da etwas gibt, was x heißt
und gibt mir das als Import.
Aber das stimmt nicht ganz.
Weil es kann nämlich auch,
man kann auch from m import x machen,
wenn es in m gar kein x gibt.
Weil eben,
wie angekündigt,
wenn m eine
Verzeichnis ist, was eine InitPy enthält.
Dann kriege ich aus dem
Import M alles das raus, was in der InitPy
drin ist. Aber from M
Import X kann dann die Datei X.py sein.
Also M-X.py
und die kann ich mit,
also an die komme ich dran,
aber die ist nicht
in M selbst enthalten. Also wenn ich nur Import M mache,
gibt es nicht M.x.
Kann so sein. Deshalb
ist es dann doch
immer noch ein kleines bisschen
anders. Also man muss eigentlich immer auf die Datei
zeigen, die wir haben.
Ja, genau, das hat mich auch überrascht.
Das wusste ich nicht, wenn man sagt,
import x, ich weiß nicht, was ich vorher gedacht habe,
aber ich dachte, das holt das x da raus
und dann hat man das halt im eigenen Namespace.
Aber eben, es macht halt eher sowas wie import m,
dann sagt es x gleich m.x
und dann sagt es del m.
Und das ist halt eher so das, was tatsächlich passiert.
Und das ist auch schon so, ui, ui, okay.
Also wenn es nicht geht, macht er doch was anderes.
Dann holt er sich doch noch die Detail.
Ja, ja, ja.
Also
es ist natürlich alles übrigens noch gecached.
Man kann so oft import m machen, wie man möchte.
Es wird nur einmal gelesen, die Datei.
Und wo wird das dann reingeschrieben?
In das Dictionary der offenen
Module?
In das Dictionary der geladenen Module.
Das heißt irgendwie sys.modules.
Ja, weiß ich auch nicht.
Keine Ahnung.
Das heißt sys.modules.
Dominik wei es Das ist auch so eine Geschichte das kann man auch sch verwenden Manchmal braucht man ja also Singleton ein ehrlich gesagt
Es hat einen total coolen Namen,
deswegen wollen es die Leute immer verwenden.
Manchmal braucht man das.
Manchmal braucht man es tatsächlich,
meistens eher nicht.
Aber Module sind immer Singletons.
Ganz genau. Und das ist tatsächlich die eleganteste Art,
die ich kenne, wie man das macht.
Und es ist auch manchmal total nervig.
Ja, aber
ich habe da vorher schon
also meistens habe ich dann Borg-Pattern verwendet
oder halt irgendwas in der Richtung
oder manchmal halt auch, wenn ich nicht Borg wollte
sondern direkt Singleton, dann wurde es aber schon hakelig
dann muss man schon
so Meta-Klassen oder sonst irgendwie sowas
so Dinge, wo man sich hinterher schon ein bisschen schmutzig
fühlt, irgendwie machen und
bis ich dann irgendwann mal, weiß ich gar nicht, wo ich das gesehen habe
den Trick, so dass jemand meinte, ja ja, Module sind doch sowieso
Singleton, es sind doch einfach globale Variablen
in einem Modul
Ja, stimmt ja
Stimmt.
Das muss man ja nochmal genau erklären, warum das ein Singleton ist.
Aber Jochen, wenn du dann das Pattern richtig ausführen
musst, musst du dann noch in dem Modul noch ein
Get irgendwas
machen.
Ja, ja.
Falls jemand versucht,
drumherum zu kommen.
Ja, genau. Eigentlich ja.
Jetzt der Dominik
möchte es gerne erklärt haben.
Also ein Singleton macht irgendwie,
dass er guckt, ob es schon ein Objekt gibt,
das dieses Typen ist und gibt das
zurück, wenn es schon gibt, und schert einfach das eine,
was es nur geben darf. Ja, Singleton heißt ja erstmal
nur, es gibt während der Ausführung des
Programms nur eins davon.
Egal, was davon ist.
Und Borg ist, dass es gibt ganz viele davon, aber die sind
alle dieselben Eigenschaften.
Ja, da wird der State
geshared, aber es ist nicht die gleiche Instanz
tatsächlich. Es können unterschiedliche Instanzen
sein, aber der State ist immer der gleiche. Genau, das heißt,
die Eigenschaften sind alle dieselben über alle Instanzen hinweg.
Ja, genau.
Und Singleton kriegt man eben durch
Modul einfach hin. Also ich mache eine Datei
singleton.py
und da schreibe ich rein x gleich 2.
Dann mache ich import singleton
und dann sage ich singleton.x
und das ist jetzt diese
Instanz, dieses .x
gibt es nur genau einmal, weil der
Python Interpreter eben beim Import
jede Datei
nur einmal importiert, um es mal so zu sagen.
Wenn eine Datei einmal importiert wurde,
dann liegt die im Cache und dann
wenn du dann import singleton sagst, kriegst du wieder
die aus dem Cache.
mit allen ihren Eigenschaften.
Das heißt,
du kriegst es mit dem...
Du kannst es nicht ganz
einfach machen, dass du diese Datei nochmal
importierst, weil Python halt sagt,
ja, die kenne ich ja schon. Brauche ich nicht nochmal
importieren.
Weil dieser Import,
was ist das? Ein Modultyp dann,
wenn du ein Modul hast? Genau, das heißt Modul.
Und dieser Modultyp ist einfach
eine Detailkette, keine Ahnung, von dem...
Ja, das Ausgeführte.
Das ist eigentlich der ausgeführte Code, der da drin ist.
so einer Closure, die außen rum...
Aber das wird dann doch gecastet,
was dann Eval wäre, oder das habe ich dann nicht genau...
Ja, im Endeffekt macht es ein Eval,
aber halt ein
sehr gut verstecktes.
Ja gut, ich meine,
Import heißt, führ mal bitte den Code
in der Datei XYZ aus, also
musst du ihn irgendwie ausführen. Irgendwo muss da ein Eval
drin sein.
Aber es ist eben hinter so vielen Schichten
versteckt, dass du es nicht siehst. Also was der macht, ist,
er lädt diese Datei... Immer wieder und immer wieder.
Das heißt, wenn ich auch von mehreren...
letzte einmal. Aber wenn ich an mehreren Stellen das habe,
was ist denn dann passiert?
Beim ersten Import Singleton wird diese
Datei geladen und ausgeführt und
das Ergebnis dieser Ausführung wird als
Module Singleton
abgelegt in diesem SysModulesCache.
Und vielleicht wird dann da PyCache
rausgebracht, PyC-Files oder so?
Ja, nee, das ist nur eine interne
Repräsentation. Das ist halt eben ein
internes, das ist eine Klasse, die heißt
Module und die hat Eigenschaften.
Ja.
Und wenn du dann nochmal Import Singleton
machst, zum Beispiel in einer Datei oder
zu einem späteren Zeitpunkt oder in einer Schleife, wo du es
tausendmal machst, dann wird
nicht nochmal diese Datei singleton.py
geladen, sondern dann guckt er eben in seinen
Sysmodules Cache und sagt, ah ja, den Namen singleton kenne ich schon.
Genau, also beim ersten Import
lädt er das tatsächlich in den Pycify
rein, also das heißt, er macht aus Bytecode draus,
er komponiert das quasi und
er liest das quasi einmal aus,
und man kippt das aus,
macht dann Bytecode draus und dann schreibt er das
in Pycache rein und von da versucht er dann
erstmal zu laden aus Pyc.
Genau.
und dann ist dieser Code nicht mehr da.
Es ist nur noch das Ergebnis da.
Das Ergebnis der Ausführung dieser Datei
ist dann das, was in das Modul reicht.
Und was wird dann in die PyC-File reingeschrieben?
Die PyC-Files sind so eine
zwischendurch Optimierung, weil
die
enthalten so einen vorkompilierten Bytecode
und der ist schneller zu laden als eine
.py-Datei. Und das ist ein
Marschalt-Objekt. Also wir hatten die ganz am Anfang
einmal kurz Marschalt gesagt. Was ist das überhaupt?
Einmal kurz nochmal den kleinen Exkurs.
Jochen, erklär du doch mal.
Ach ja, damals. Genau, im Grunde Marschaling ist eigentlich ein anderes Wort oder ist eines der Worte, die man benutzt, wenn man jetzt zum Beispiel Code oder irgendwie eine Datenstruktur serialisieren möchte in eine Liste von Buchstaben oder in einen String oder in eine Byte-Datenstruktur, die man dann halt irgendwie auf einer Platte speichern kann oder mit sich rumtragen.
und genau, und da gab es zwei Module, die ersten waren halt Marshall,
das heißt auch so, das gibt es immer noch in der Standardbibliothek,
und Shelf, Shelf war schon damals nicht so richtig populär,
also Marshall kann man immer noch verwenden, kann halt nur relativ wenige Datentypen
und in neueren Python-Versionen verwendet man da eigentlich immer Pickle für,
aber das ist halt im Grunde das, was es macht, wenn man sagt,
vom Pickle, oder wenn man sagt cpickle.dump, dump-s oder so,
dann wird das halt in String oder dump in einen Teil geschrieben
und dann hat man halt eine in einem String
serialisierte Form von zum Beispiel irgendwie
Code. Also mittlerweile nutzt man glaube ich sogar
Dill oder sowas, ne?
Dill as Pickle oder so,
als neue Version. Ja. Als Third-Party-Package
irgendwie, ja. Schön, schön.
Können wir auch machen. Okay, aber ein Marschall-Objekt,
falls ihr das irgendwo findet, ist quasi eine serialisierte
Form von
dem, wie es heißt, Code, der da
gelaufen ist, ja. Genau.
Okay, jetzt haben wir aber, okay, jetzt haben wir es irgendwo geladen,
haben wir es gefunden. Was importiert ihr dann?
Was ruft ihr dann auf? Ja, das
Ergebnis dieser Ausführung.
Also import.dot, dann dann import.
Dann dann.
In diese Funktion kommen
dann halt die Argumente rein, die
Ja, das kann ja alles möglich sein.
Da kannst du auch beliebige Objekte
in der Datei reinschreiben.
Kannst auch ein Modul machen, was einfach nur einen String enthält,
wo irgendwelche Binärdaten drin sind.
Macht man ja manchmal,
weil man Testdaten braucht.
Das Modul hast, wo einfach
so ein paar Strings drin sind. Und wenn die
eben ausgeführt werden, dann werden die halt so einer
String-Objekt in Python
mit einem Namen und genau diese
Sachen, also im Endeffekt ist das halt so
ein Dictionary, was da drin ist, so ein Unterstrich, Unterstrich,
Dikt, Unterstrich, Unterstrich.
Das hat natürlich noch mehr so Attribute, ja.
Der Path ist da drin
und der ursprüngliche
Importname und der Docstring
wird rausgelesen und so weiter. Also diese ganzen
Sachen, die halt so passieren, wenn Python was
ausführt,
die passieren da auch. Alles das, was Python langsam
macht. Alles das, was Python awesome
macht.
Genau, und das Ergebnis dieser Ausführung ist halt dann das, was das Modul ist.
Das zusammengefasste Modul. Da gibt es Modul.doc und Modul.tikt und Modul.
was weiß ich noch alles, .name und .path und .package
Genau, also alle diese Sachen, die halt dazu gehören. Aber das ist dann schon ein Python-Objekt.
Also es ist keine Datei mehr, sondern es ist dann ein fertiges Python-Objekt.
Und auch diese Klassen, die müssen ja erzeugt werden
und wenn es so Meta-Klassen hat, dann müssen die ausgeführt werden. Also alles das, was da
wenn man die Datei in den Interpreter eingibt
und dann alles was da rauskommt
eben zusammenfasst
in eine Sache gepackt
also es ist eigentlich von der Organisationsebene her
finde ich sehr sauber
weil alles was in der Datei drin ist
ist dann eben als Python Objekt verfügbar
und das ist auch für mich so ein bisschen
der Grund warum man das braucht
dass ich halt einfach bestimmte Python Objekte
oder bestimmte Dinge in andere Dateien
reinschreiben möchte
und diese Dateien sollen in anderen Verzeichnissen liegen,
damit ich nicht die Übersicht verliere.
Und ja.
So, das ist Import.
Und jetzt
gibt es noch Importlib.
Ja, das wird ja
aufgerufen sogar, also Import wird ja Import,
auch wenn man es nicht überschrieben hat.
Import ist eigentlich syntaktisch, genau, das kommt ja
auch noch dazu, das ist ja in dem Artikel
glaube ich nicht drin, aber in der Diskussion war es dann drin,
man kann das auch überschreiben, man kann auch
selber eingreifen in den Importprozess.
also Dinge, die man
auf der Liste der Dinge, die man unbedingt tun sollte
ist das ganz, ganz
weit unten
Es gibt
Importlib und in Python 3 irgendwas
hat auch
dieser Importprozess ist quasi
zu einer Python-Funktionalität geworden
den man programmatisch
aufrufen kann, was sehr nützlich ist
und
diese Statement
Import M ist quasi nur noch
ein syntaktischer Zucker für
importlib.
.import
Ja, irgendwie sowas.
Das
coole daran ist, dass man
eben jetzt in diesen Prozess eingreifen kann.
Und das ist insbesondere dann für zwei Sachen nützlich.
Die erste Sache, für die es super nützlich ist,
da muss ich jetzt leider auf meine eigenen
Dinge verweisen, da werde ich
in Kürze ein Video dazu hochladen.
kommt dann natürlich in die Show.
Wenn man etwas
neu laden möchte,
weil
wir haben ja eben besprochen, wenn ich Import M
mache, dann wird es einmal geladen
und dann nie wieder.
Man ist ja jetzt oft als Softwareentwickler in so einer Situation,
dass man Dateien dann verändert und die
eigentlich sofort
haben möchte, ohne dass man das Programm
neu startet. Und um das hinzukriegen,
habe ich mir halt so ein bisschen
einen Glue-Code geschrieben, der Dateien
überwacht und wenn die sich ändern, dann wird da halt
Reimport durchgeführt.
Und das ist möglich durch die Funktion
aus Importlib. Da kann ich sagen,
importiere diese spezifische Datei,
auch wenn
die schon mal importiert war,
und dann
wird die importiert. Egal, ob die schon im
Cache liegt oder nicht. Ja, cool. Ja, ich meine,
der Entwicklungshelfer von Junkmo,
der kann sowas ja auch. Ja, aber der macht ja
einen Neustart. Der macht tatsächlich einen Neustart, naja.
Der hat so einen Watcher-Prozess und dann
schießt er einfach den Prozess ab und startet einmal neu.
Ach so, ich weiß gar nicht, wie das
gut, das ist ja, oh mein Gott, das ist ja schrecklich.
Ja, aber es hat auch Vorteile,
weil dann halt alles
auf Null zurück ist.
Wie ist das denn bei
Jupyter, ist das ja auch so, da kann ich ja auch beim
Import sagen, ob ich das jetzt möchte,
dass ich das nochmal importieren will,
wenn es sich ändert. Ja, dann mach doch ein Reimport.
Da gibt es eben diese Jupyter Magic,
die du sagen kannst, und der benutzt
tatsächlich Import-Lib und macht Reimport.
Aha, okay.
Neustarten wäre auch blöd.
an der Stelle.
Geht ja auch an der Stelle einfach gar nicht.
Zum einen, weil der Prozess ja da
läuft und zum anderen, weil
der die anderen Zellen nicht kaputt machen will.
Das heißt ja, die Zellen immer noch.
Ich hatte übrigens tatsächlich jetzt
letzte,
vorletzte, nee, letzte oder vorletzte, nee, glaube letzte
Woche, und ich erinnere mich schon nicht mehr dran,
den Fall, wo ich tatsächlich Importlib gebraucht
habe und habe halt die Importfunktion
überschrieben.
Was hast du damit gemacht?
für einen, ich habe einen
Pull-Request bei Wagtail Media
eingereicht, weil
es nicht mit Wagtail 2.13 kompatibel war.
Und da gibt es Tests
und das Problem ist jetzt,
man möchte die Tests
ja, also da ist
dann, okay, ich fange einfach mal von der Seite
an, es gibt im Code so Dinge wie
probier das mal zu importieren,
wenn es schief geht, mach was anderes, weil
zum Beispiel diese Telepath
Bibliothek gibt es erst ab Worktel
2013.
Und da sagt man halt, probiere es zu importieren.
Ja, auch für lokale, ich benutze es ganz häufig für lokale
Settings, ja. Irgendwo eine local settings.py
und dann macht man halt
try import local settings.py
und accept pass.
Ja. Genau, genau.
Und jetzt müsste man das aber testen.
Jetzt möchte man testen, dass wenn
und es gibt da noch kompliziertere,
wie gesagt, das habe ich jetzt wieder vergessen, blöderweise, ein bisschen kompliziertere
Logik, die davon abhängt, ob man es jetzt geschafft
hat, das zu importieren oder nicht.
und jetzt, wie willst du das testen, ob jetzt wirklich der richtige Code ausgeführt wird, also der Code, der zum Beispiel dann nur so ein Dummy-Objekt anlegt,
wie willst du testen, ob das funktioniert hat, wenn du nicht dann noch eine andere Paketversion installieren willst, was du im Test auch nicht gut machen kannst?
Ja, und dann habe ich da einfach quasi Import-Funktionen überschrieben und dann in der Import-Funktion gucke ich halt so, wird gerade versucht, das da zu importieren?
Ja, wirklich? Gut. Race-Exception, ja, und dann Race-Import-Error.
und dann kann man halt testen, okay, und ist jetzt wirklich das Sammely-Objekt, ja oder nein?
Und dann hat man einen Test dafür.
Ja, aber fand ich spannend.
Das ist auch ganz schön tief eingegriffen.
Ach, der Jochen auch aufgenommen?
Ja, genau.
Du musst jetzt aber suchen, in welcher Folge das war.
Kannst du uns da einen Link geben, Jochen?
Kann ich auch mal in die Schauenden folgen.
Das ist ja cool.
Ja.
Ja, was kann denn noch alles dann passieren?
Genau, das ist jetzt sozusagen, der Jochen hat jetzt hier schon die Brücke zu dem zweiten Anwendungsfall geschlagen,
was dann eben hier aus diesem Artikel in der Diskussion
rauskam, man kann auch
im Importsystem was anderes
machen
und der schöne Vorschlag, der da kam
beziehungsweise das schöne Beispiel, was da in
dieser Diskussion kam, ist
dass man ja prinzipiell auch andere Sprachen
integrieren könnte. Einfach Importsystem auf einen ansetzen
Das kann man auch machen, dann ist man in
seiner eigenen Welt
aber quasi das Gegenteil
davon ist, wenn kein Python-Modul
gefunden wird, dann kann man ja mal schauen, ob man irgendwas anderes
kompilieren kann, was diesen Namen hat
und wenn das
kompilierbar ist und kompiliert, dann einfach das
importieren. Und es hat wohl jemand
geschrieben, dass jemand sich eben so Lisp-Dateien
rein importieren kann, die
live kompiliert werden, wenn man
sie importiert.
Reichlich abgefahren, würde ich sagen
und ich empfehle auch niemandem, diesen
Weg zu gehen, aber es ist
schön, dass es jemanden gibt, der das
gemacht hat und das möglich ist.
Ich wüsste
jetzt tatsächlich keinen richtig sinnvollen
Anwendungszweck davon, aber
Ja.
Irgendwann wird es
einen Anwendungsfall geben.
Ich habe gerade überlegt,
wenn man das jetzt mit TypeScript macht,
man möchte auf dem Backend
und vorne den gleichen Code.
Ja, und dann wird es noch
mit dem JavaScript to Python
Compiler noch Python gemacht.
Dann hat man noch einen Server,
was WebAssembly kann.
Man kann Python schreiben für Frontend.
Und schon
eigenes, die eigene, deine Dateien bestehen quasi nur noch aus Import Statements. Alles mit Import Statements gemacht. Ja, das ist so wie der Präprozessor, der ist auch Turing vollständig.
Ja, tja.
Da hat jemand super wieder sein...
Vorhin haben wir noch drüber gesprochen, ob wir unser Telefon ausschalten wollen.
Flugmodus gestellt, aber nicht
mein Rechner.
Auf meinem Telefon hat es auch nicht geklingelt, sondern
mein Rechner kann halt auch dummerweise klingeln.
Was kann denn noch alles schief gehen bei den
Importen? Ja, eigentlich kann beim Importen alles
schief gehen, oder? Das ist ja...
Ja. Da steht irgendwas
komisches drin, also keine Binärcodes oder irgendein andere
Zeichen, das er nicht lesen kann oder sowas.
Ja, es kann auch...
Ich glaube, das Schwierigste an diesem Importsystem
ist, dass es halt manchmal überraschend ist.
Dass es einem Sachen versucht zu importieren,
die man nicht gemeint hat.
weil die Reihenfolge im Path falsch ist
oder weil da Module liegen, die man noch nicht kennt
oder weil man versehentlich
einen Namen verwendet hat, den es schon gibt.
Also wenn man so eine Kette durchgeht,
also man möchte jetzt A importieren und A
möchte aber jetzt C, D, E, F, G und
Genau, sowas kann natürlich auch passieren, dann hast du zirkuläre
Imports, das ist auch schlecht.
Ja, aber das wäre ja von A von B und B von C
und C von A. Ja genau,
und das hat man ja manchmal.
Aber das war ein anderes Problem, als das ich gerade meinte.
Das ist also ganz, ganz, ganz viel Zeug.
importiert. Also du könntest ja quasi eine Sache importieren, der importiert
alles andere, die ganze Standard-Python.
Im Prinzip machst du das ja so in deinem Skript.
Das ist halt so relativ getarnt,
was da passiert. Man macht relativ viel
Magie schon.
Die komplizierte
Frage wäre, ob das das dann doch langsam macht,
wenn man sowas raussucht und sagt,
beim ersten Mal schon. Das heißt, da kann der Starten
relativ lange dauern und beim zweiten Mal
vielleicht nicht. Aber es gab irgendwie
so ein Problem, dass irgendwer hatte, der das
mal ausprobiert hat wegen den Imports.
immer wieder. Ja, ja, ja. Also das Problem
gibt es halt, wenn du halt irgendwie so auf die Richtung
weiß ich nicht, Millionenzeilen
Python oder so zugehst, dass eben dann, wenn das
zum Beispiel im Entwicklungsserver, wenn der neu startet,
wenn du änderst irgendwas, dann startet der neu
und dann dauert das halt eine Minute.
Das ist halt schlecht. Das ist halt immer so richtig
flüssig beim Entwickeln. Es hieß ja
irgendwie so, ne, wir haben das Google, aber ich glaube,
das war bei Instagram das Problem oder so.
Ja, bei Instagram gibt es auch bei
Eventbrite gibt es das, glaube ich, auch.
Ja, die sind nicht so groß wie Google.
Ja, aber die haben, glaube ich, auch irgendwie
oder
verwechsel ich das gerade?
Ich meine, es ist Eventbrite, aber auch ungefähr eine Million
Zahlencode und Instagram halt auch
vielleicht noch mehr, weiß ich.
Ja, okay. Aber das ist ja dann so
ein...
Das Problem ist ja eigentlich, dass halt die Sachen
erst so in Anführungszeichen kompiliert werden,
wenn ich Import sage.
Und deshalb haben andere Sprachen das nicht, weil die
machen das halt einmal vorher.
Aber so eine richtig gute Lösung,
also ich meine, es gibt PyO-Dateien
und PyC-Dateien,
die sind ja wohl schneller
ja
oder könnte man
sowas auch marscheln
könnte man Imports durchführen
und das dann, ja im Prinzip schon oder
wahrscheinlich schon ja
einfach mal alles gepickelt
ja
ach deswegen auch weil der Pickel ist die Haube von dem Marschall
jetzt verstehe ich das auch
ich dachte Pickel ist so ein Einmachglas
Marschalls haben doch so eine Pickelhaube auf
ja, also dann passt das auf jeden Fall
zusammen
Das ist doch zumindest schon irgendwie
Sorry, weigert es
Das Englische, wo Pickels heißt, eingemacht ist
Ja, ja
Ich glaube, das ist die
Okay, aber zirkuläre Importe wollte ich nochmal
Ja, zirkuläre Importe sind auch eine schöne Sache
Also man kann ja sowas machen wie in Python
Form A, Import B und dann
B machen, Form B, Import irgendwas
oder Import A, Import B, Import D und so
und das funktioniert einfach, aber erst wenn man es dann ausführt
dann nicht mehr, weil er dann darauf zugreift
und dann sagt, das kennt er noch nicht
Genau, beziehungsweise ist schon geladen
oder ist schon am Laden.
Also wenn ich
zirkuläre Verweise
habe, vom A Import B und in B
sage ich vom, wenn ich
Modul A und B habe und in A
sage ich Import B und in B sage ich Import A,
dann kann ich die beide nicht importieren, weil zu dem
Zeitpunkt müsste ich sie beide gleichzeitig
importieren und das geht nicht.
Und da kann ich natürlich beliebig
viele dazwischen schieben. Ich kann A, B,
C, D, E, S. Solange dieser
Kreis sich irgendwann schließt,
ist schlecht.
Also es fällt ja direkt auf. Ich glaube, wenn man drei hat,
dann fällt es glaube ich nicht immer direkt auf.
Wenn man vier oder fünf hat, dann fällt es erst recht.
Das siehst du dann beim
Importieren. Und wie löst man das denn, wenn man
so ein Problem hat?
Laufzeit-Imports.
Also lazy quasi.
Das ist doch das, was man in so
Salary-Tasks immer machen muss.
Weil diese Tasks können von irgendwo her
gestartet werden und die sollen dann irgendwas machen.
Und die müssen
ja auch irgendwie diese Modelle importieren, aber das Modell
selber m Also das ist so mein klassischer anwendungsweise Ich m dem Modell sagen ich muss hier Werte nachberechnen ich muss hier ein PDF erzeugen und das dauert lange und deshalb mache ich
das in dem Task. Aber dieser Task soll ja dann wieder
dieses ursprüngliche Modell speichern.
Das heißt, ich kann in der Tasks-Datei
nicht das Modell importieren,
weil das Modell hat schon den Task importiert.
Und deshalb ist das so ein
Pattern, wenn man Celery-Tasks schreibt,
dass diese Tasks
zur Laufzeit alles importieren. Also
in der Datei, in der Funktion
dev compute pdf.
Da habe ich erst mal ein paar
Importzeilen drin. Import from models
import irgendwas.
Import pdf generator.
Und noch alles, was man da halt so importiert
braucht. Und dann werden diese
Imports erst ausgeführt, wenn die
Funktion gestartet wird. Also von mir, von meinem
geistigen Auge, die ich mir gerade so ein paar Tickets ziehe.
Die To-Do-Listen müssen ja an.
Genau.
Das bedeutet halt, dass dieser Import nicht
ausgeführt wird, wenn die Datei geladen wird.
Was eben der
Fall wäre, wenn es oben in der Datei drinstehende,
sondern dieser Import wird erst ausgeführt,
wenn die Funktion, die da drin ist, gestartet wird.
Weil quasi nur der
Funktionsname geladen wird und das, was
passieren soll quasi. Genau, richtig.
Und wenn ich diese Funktion dann ausführe,
dann wird dieses Import Statement ausgeführt.
Und dann sind die anderen Sachen schon da
und dann hat man quasi den Laser Import, dann kann man
das dann wieder machen, weil die sind ja schon...
Das ist dann schon fertig, importiere diese Models, die sind schon im Cache,
dann kann ich einfach in den Cache zeigen und sagen, hier gibt man das.
Bin also
sozusagen aus diesem Prozess raus.
und das ist auch sowas, was eben
anders ist als in anderen Sprachen.
Import ist ein Statement, was einen Effekt hat.
In C, wenn ich
in C sage, import header.h,
dann hat es keinen Effekt,
das ist kein Code, der da erzeugt wird.
Punkt H ist immer Header,
noch mal für alle Leute kein C.
Punkt H ist immer Header, das sind so die
Definitionen, die Deklarationen,
nicht die Definitionen, die Deklarationen, was es da gibt
und die Punkt C Datei enthält
dann die Definitionen normalerweise.
Also Header.h ist natürlich die Definition der Header.
Die Definition,
da muss ich dann eine
Datei dazugeben, die heißt
Header.c
War nur ein Beispiel.
Aber
dieses Import-Statement in einem c,
das wird vom Compiler ausgeführt, das wird nicht
zur Laufzeit ausgeführt.
Und in Python ist das anders.
Der Compiler, der macht das,
der schneidet die einfach zusammen, alle zu einer großen Datei
und dann liest der einfach von oben nach unten.
und dann gibt es noch den Linker, der dann dafür
sorgt, dass diese Verweise alle an der korrekten Stelle
in die korrekte Stelle zeigen. Das ist so ein
mehrstufiger Prozess und es ist halt alles
bevor das Programm gestartet wird.
Und in Python ist immer alles
nachdem das Programm gestartet wird.
Und das ist halt hier genau das Problem. Wenn ich
Import B sage und
versuche aber schon was zu importieren
oder versuche schon B zu importieren,
dann geht es halt nicht.
Weil das dann eben so einen Kreis
bildet, der nicht aufgelöst werden kann.
Wenn ich das aber erst später im Programm sage,
wenn ich irgendwelche Funktionen geladen habe,
die dann später nochmal
diese Module brauchen.
Ist egal, wo die herkommen.
Wenn die schon im Cache sind, einwandfrei.
Okay, aber das widerspricht ja so ein bisschen dieser
Philosophie, dass Importe immer ganz oben stehen.
Ja, genau. Deshalb ist das hier so der
Nachteil da dran.
Ja, ich fühle mich da auch immer so ein bisschen schmutzig, aber
ja, ich mache das auch so. Aber es ist halt das Pattern
und das ist der beste Weg außenrum.
Es gibt noch eine andere Möglichkeit,
die ich auch
niemandem empfehlen möchte. Es ist ein sehr schöner Hack,
den ich mal gehört habe.
Wenn ich jetzt zum Beispiel nur einen Task importieren möchte,
dann sage ich from tasks import
greatpdf.
Und der Importprozess führt
tatsächlich nur so lange diesen Import
aus, bis dieses Objekt vorhanden ist.
Alles was unterhalb dieses Objektes ist,
wird
nicht ausgeführt.
Das heißt, wenn ich meine Importstatements
ans Ende einer Datei mache,
dann kann ich zirkuläre Importe
machen,
in bestimmten Situationen, dann geht
das.
Aber
das, wie gesagt, ist keine
Empfehlung. Es ist nur
Imports ans Ende der Datei schreiben ist
schon generell schlecht, weil man sie dann nicht mehr sieht
und es ist auch dann schlecht, wenn man dann
zirkuläre Importe... Das kann man dann wahrscheinlich auch noch
ein bisschen verschachteln oder so. Also alle Leute, die
ein größeres Interesse
an Job Security haben, jetzt mal
aufpassen mit Schreiben.
Das kriegt man hinterher nie wieder aus
und wenn man dann noch
Homoglyphen nimmt und die Dateien
alle gleich ausschauen, dann
hat man ganz gewonnen.
Ja, das ist ein Hack, der
die Interna missbraucht, würde ich nicht empfehlen,
aber es ist schön, dass es das gibt.
Ja,
ist auch manchmal interessant
zu wissen, warum irgendwas funktioniert, wo man
denkt, das kann gar nicht funktionieren, aber es funktioniert
dann doch.
Ja, ja.
Nicht so selten, wie es funktioniert, was nicht, was eigentlich funktionieren sollte, aber trotzdem auch.
Leider.
Ja.
Ja, Imports haben wir, oder?
Ja.
Erklärt, oder fällt euch noch was ein?
Bin mir sicher, dass da noch ganz viele Fragen kommen.
Vor allem, wie funktionieren Imports in anderen Sprachen?
Ja, anders.
Ja, das nicht so genau.
Ich bin mir sicher, dass da ganz, ganz, ganz viele Fragen noch offen sind und dass da ganz, ganz, ganz viele Fragen noch kommen werden.
aber so. Ich glaube, den...
Ein bisschen als Einstieg, weil, also ich meine auch,
mir waren da viele Dinge
nicht so richtig klar. Also jetzt, nachdem ich den Artikel
zumindest teilweise so gelesen habe,
dachte ich, ja, okay, da kann man schon coole Sachen mitmachen
und vor allen Dingen kann man das auch,
dass das eben, dass ein Modul auch nur einfach
ein Objekt ist, das ist irgendwie die Eigenschaft,
kann man ja eigentlich total gut ausnutzen, um damit
irgendwie Dinge zu machen.
Aber sollte man.
Sollte man vielleicht nicht, aber,
also zum Beispiel ein tatsächliches Problem,
wo ich halt auch schon so mit Paketen
und Dingen rumgespielt habe, wo ich dachte so,
oh, da gibt es, irgendwie gibt es da keine gute Lösung
für, aber es wäre schön,
eine zu haben, ist halt, oder ich weiß nicht, vielleicht kennst du
eine, ich meine, im Machine Learning-Bereich,
wenn man da Modelle hat, man trainiert die Modelle
jetzt aber nicht, also man hat normalerweise ein
Produktionssystem, was irgendwie dann Dinge
tut, so mit echten Usern und
echten Daten und so,
aber da
entwickelt man ja nicht und man trainiert ja auch nicht
die Modelle, sondern Modelle trainierst du dann meistens irgendwie
in einem Jupyter-Notebook oder so,
dann hast du da dein Modell
und jetzt willst du das irgendwie
aber in deine Produktionsumgebung integrieren.
Wie machst du das denn? Jetzt kannst du
natürlich jedes Mal, wenn du dein Modell trainiert hast,
aber das sind ja oft Dinge, die dann auch periodisch passieren
und automatisch, kannst du
ein neues Paket bauen und das dann releasen.
Aber dieser Deployment-Prozess
ist halt ziemlich invasiv.
Vielleicht willst du
das nicht regelmäßig oder auch nicht automatisch
machen, weil du brauchst Verbrechtigungen
auf irgendwelchen Systemen und keine Ahnung.
Gut, kannst du das natürlich auch, kannst du irgendwie
Deployment automatisieren, was halt so ein bisschen höhe ist.
Und du hast dann ganz, ganz
viele Paketversionen auch
und es ist halt viel,
diese Modelle sind halt teilweise sehr riesig.
Also irgendwie
ist das nicht so richtig schön und
das ist halt die Frage, wie macht man das eigentlich ordentlich?
Und du hast das Problem, wenn man jetzt Sachen pickles,
zum Beispiel auch,
dann ist das
von dem Python-Interpreter abhängig
und so
und der ist möglicherweise in diesem
Jupyter-Notebook-Umfeld, wo jetzt
Data Sciences, irgendwelche Modelle trainieren, halt auch wieder
anders, aus speziellen Gründen anders
als in der
Produktionsumgebung, wo dann die...
Und Pickel kann ja selbst auch Imports durchführen.
Ja.
Du musst ein bisschen aufpassen, wenn man
Versionen nimmt oder so, also Sachen pickelt und dann
Python-Versionen weitergeht, dann geht
das dann mal kaputt, das heißt, das ist nicht mal so einfach
dann... Ja, beziehungsweise auch, wenn du andere Imports
hast oder andere Versionen, Pickel
importiert halt die Sachen, die es braucht und
ja.
Ja, und also das ist
Das kann schon, also da gibt es
diverse Fallstricke und der Weg, den ich dann
irgendwann gegangen bin, ist halt dynamisch
Conda-Pakete zu bauen und weil das
mit Conda relativ leicht ging, aber das war
da kann, ich weiß nicht, ob es da Importlib, doch
Importlib gab es glaube ich auch schon, ich wusste aber nicht so viel
davon, ich weiß nicht genau
und die habe ich dann halt sozusagen als
Django-Modelle
irgendwie als Files irgendwo hingelegt und dann
diese Pakete importiert und dann konnte man einfach
einfach sozusagen sich, konnte man
in einem Jupyter-Notepunkt, wenn man ein Modell fertig trainiert hat
konnte man sagen, deploy mir dieses Modell irgendwie
und dann tauchte das halt auf
und wenn man es ausgewählt hat, wurde es halt importiert.
Und zwar einfach aus...
Aus der Datenbank importiert.
Aus der Datenbank, beziehungsweise nicht aus der Datenbank, sondern
aus der Datenbank plus irgendwie,
naja, nicht in dem Filesystem, sondern in der Filesystemabstraktion
im
Files storage,
und
das geht wahrscheinlich auch
ohne Conda, nehme ich mal an,
wenn ich das jetzt mir so überlege.
Und wahrscheinlich geht das auch noch eleganter,
als wie ich das damals gemacht habe.
Ja, aber also jetzt mal die Diskussionsfrage, warum nicht wirklich aus der Datenbank importieren? Im Prinzip sind da nur irgendwelche Daten, kannst du da als String da reinlegen und kannst sagen, importiere mir dieses Modell, kannst du ein Reimport machen und dann wirst du vermutlich noch so einen Proxy-Pattern brauchen.
Ja, also das Ding für
Conda war dann halt tatsächlich
irgendwie, was ist, wenn ich jetzt irgendeine spezielle
Abhängigkeit habe
in meinem Modell, also ich benutze jetzt irgendeine
neue fancy Maschine
in der Bibliothek, die ich auf dem
Produktionssystem jetzt aber noch nicht habe
Ja, okay, dann stirbt es ab und
Genau, und dann ist halt
kaputt, wie kriege ich das
da mit rein und zwar ohne, dass ich
ja, und das war, über Conda ging das
aber
Ja, okay.
Das ist natürlich schwierig, vor allem wenn du dann Sachen installieren
musst, die du eigentlich, was du an der
Stelle eigentlich nicht willst. Wobei,
also ich meine, Pip gibt es auch als Bibliothek.
Du kannst auch Pip aufrufen
von Python heraus.
Also,
was ich für
meine
interaktive Programmierungssache gemacht habe, ist
eben so ein Proxy-Pattern.
Dass du quasi
eine Schicht dazwischen hast.
weil du möchtest ja
an der Stelle, wenn du es neu importierst,
musst du halt auch ganz viele Instanzen ersetzen.
Eigentlich. Und das ist
schwierig, weil die hast du oft nicht selber in der Hand
oder da kommst du oft einfach gar nicht dran.
Also Instanzen, das heißt, du musst die laden mit dem neuen Kontext
der neuen Importe. Genau, weil
du den neuen Code hast und eigentlich
müsstest du jetzt die... Du musst aber dann quasi ja alle
Transaktionen, die du auf der einen Instanz hast, schon gemacht hast,
schon wieder nachvollziehen, weil du den State ja quasi wiederherstellen musst.
Genau, also genau, das ist die eine Sache,
du müsstest den State quasi wiederherstellen und die andere
Sache ist, du müsstest den Code ersetzen, weil
Ich habe jetzt ja ein m.x und ich möchte aber ein m.x2 haben.
Aber das kann jetzt ganz anders sein, weil du hast jetzt nur m.
Ja, genau, kann sein.
Und dafür gibt es einen Pattern, das heißt Proxy-Pattern,
wo du quasi ein Interface definierst, was da alles drin ist.
Was ist denn ein Interface?
Also du definierst quasi, was ein Objekt kann.
Also ein Typ.
Und die eigentliche Umsetzung ist aber quasi eine Klasse dahinter.
Du definierst quasi den Typ und dann...
Dieser Proxy macht nichts anderes, als diese Aufrufe weitergeben.
und dann kannst du nämlich dem Proxy sagen, ersetz mal deinen Background,
ersetz mal deinen Hintergrund, den Code, ohne den Proxy selbst anfassen zu müssen,
den du halt oft nicht rauskriegst, weil der in irgendwelchen Sachen drin hängt.
Und ich könnte mir durchaus vorstellen, dass das mit so Machine Learning Modellen auch funktionieren könnte,
dass du halt einen Proxy hast, der dieses Modell weg abstrahiert und der kann die Sachen irgendwoher wiederladen,
ob das jetzt aus der Datenbank ist oder aus dem Dateisystem oder sonst woher.
einfach, dass du eine Trennungsschicht drin hast.
Ja, das ist wahrscheinlich auch
der richtige Weg.
Das wäre der richtige Weg.
Ja gut, man weiß das ja
oft zu dem Zeitpunkt nicht so richtig.
Es ist einiges an Verwaltungsaufwand,
so ein Proxy richtig zu machen und
das geht in manchen Sprachen einfacher.
Ein Interface definieren geht
in Java ganz einfach, weil das gibt es da halt.
In Python ist es halt nur eine Klasse,
die lauter Not Implemented Errors hat.
Ja, aber ungefähr so, ja.
Ja.
Ja.
Achso, gibt es zu diesem Projekt, gibt es da eigentlich auch schon einen Link oder sowas?
Nee, demnächst.
Demnächst.
Wird nachgereicht.
Ich liefere das nach.
Okay.
Und jetzt habe ich es öffentlich gesagt, jetzt muss ich es auch wirklich machen.
Das ist immer gut.
Sehr schön.
Ich hatte ja, vielleicht rede ich mich ja noch gegenüber mir selbst, kann ich mich immer
sehr leicht rausreden, aber wenn ich es jetzt schon gesagt habe.
Ja, gib mal auf jeden Fall deine Bucketlist, dann erwähnen wir es mal öffentlich.
Ja, gut.
Das ist gut.
Ja, also interessant, dass man aus so einem Blogartikel
noch alles machen kann. Also wir hatten ja, glaube ich, ja vorher, bevor wir
einen Blog gefunden hatten, irgendwie, und dann war ja irgendwie
ergänzte Information. In demselben Blog habe ich
noch so ein paar andere Sachen entdeckt, die wir auch vorhaben,
sowas wie Datentypen sprechen und wie das implementiert
ist und wie halt das Python-Objekt-System
funktioniert oder die einzelnen Dinge oder
der C-Python-Compiler,
wie das überhaupt geht. Und da müssen wir auf jeden Fall
auch mal drüber sprechen. Aber schaut doch schon mal
in diesen Blog rein, 10.000 Meters Com, das fand ich wirklich
interessant. Ja, da waren einige gute Sachen dabei.
Ja, ziemlich.
Ja, ich habe mir auch dieses
C-Python-Internalist-Buch
besorgt.
Ja,
mal schauen.
Vielleicht kann man da auch noch ein bisschen was rausholen.
Ja, das werden wir auf jeden Fall, also vielleicht
verpusten wir jetzt tatsächlich so jeden dieser Blogposts.
Wir lesen die einfach vor.
Nein, das ist wirklich toll und ich glaube,
dass viel, was auch Hörer von uns gerne
interessiert und wissen wollen, was da irgendwie so passiert,
dass man so ein bisschen darüber redet, weil das machen wir ja.
So ein bisschen erklären, wie Python
als Sprache und so.
Ja, auch.
Ja, spannend.
Ja, super. Ich glaube, wir haben
über Impulse gar nicht mehr so viel zu sagen.
Ich habe gehört, Johannes hat sich extra einen Pick der Woche überlegt.
Ich habe mir tatsächlich einen Pick
und zwar GitHub Octo.
Das ist eine
Anwendung, die GitHub geschrieben hat,
mit der man Repositories
visualisieren kann.
Also die interne Struktur des Codes
in einem Repository.
so der Hintergrund ist
du kommst in ein neues Projekt rein
und da sind jetzt eine Million
Teilen Python-Code drin
und deine erste Aufgabe ist
mach mal hier eine Log-Ausgabe rein
es ist sehr schwer
sich da zurechtzufinden und es ist auch sehr schwer
Sachen zu finden in Projekten
wenn man sie nicht sehr genau
kennt und da ist eben GitHub
Octor, das macht hier so ein paar sehr schöne
Grafen, das zeigt quasi grafisch
und ich kann es jetzt mal ins Bild halten hier, zeigt quasi grafisch die Abhängigkeiten
zwischen den verschiedenen Dateien, wo die liegen und wer sich importiert und wie die
aufeinander aufbauen und dann kann man eben sehr schnell da so einen Überblick bekommen
darüber, was mit was interagiert und was wohin gehört.
Also schöne Kreisbeispiele hier zeigen.
Wundervoll, das sieht ja richtig kreativ aus.
Es sieht richtig schön aus.
Zum Ausdruck an die Wand hängen, also als Kunst jetzt.
Ja, zum einen das und zum anderen auch funktionale Kunst, wo man eben nachsehen kann, von wo nach wo man hin navigieren muss. Und auch interessant, weil es eben so auf eine gewisse Art und Weise die Architektur einer Software widerspiegelt, die ja sehr unterschiedlich sein kann. Also wenn man sich hier diese Screenshots anschaut, die sind ja zum Teil sehr unterschiedlich. Manche sind halt sehr flache Hierarchien.
Die Screenshots werden wir auch nehmen.
Sehr breit, selbstverständlich.
Und manche sind sehr gut aufgeteilt. Da sieht man vielleicht auch so ein bisschen den Style.
Genau, also es ist wirklich einfach
eine Einsicht, die man sonst nur sehr schwer
kriegen kann. Oder auch hier, also
hier gibt es eins,
wo man eben sieht, dass
jedes Paket quasi gleich ist und jedes Paket
hat auch so eine Testpunkt-Pi drin und das sind eben
halt diese grünen Punkte hier und man sieht, okay,
jedes Paket hat eins. Oder in einem anderen
Projekt sind die grünen Punkte halt alle in einem
großen Modul drin und
man sieht, okay, die sind nebendran, die sind offenbar nicht
integriert. Also es gibt einfach sehr
schnell einen Überblick über die Struktur
eines Projektes, was so an sich
nicht so einfach zu finden ist.
Das ist mein Pick
für diese Woche.
Ich würde picken Oh My Git.
Das ist ein Spiel,
wenn man Git lernen will.
Oh My Git?
Ich weiß gar nicht, was daraus geschrieben wurde.
Aber man kann es halt damit Git spielen
mit so Cards und lernt so ein bisschen über Git.
Natürlich auch so Beginner-Level und so, aber das ist halt
relativ spannend, wenn man so ein bisschen Advanced-Git
noch machen will. Also, wenn es nicht jetzt
Rebase Interactive und dann irgendwie gucken will,
dass man irgendwelche Commits stasht, dass man irgendwie
dann doch irgendwie die Message ändern und
was man da nicht so alles machen kann, so ein bisschen zu verstehen,
was das überhaupt ist. Cool, hört sich
aufspannend an. Sehr nice, ja, also
Gamification mit GIT
halt als kleines Spielchen ist schon irgendwie cool.
GITification. Ja, man sieht halt
auch immer ein bisschen visualisiert und man kann da so Sachen klicken
und das ist irgendwie ganz nett, also weil
ich meine, so Bratis oder so, jemand, der so ein bisschen programmiert
oder hat das ja irgendwann schon mal gesehen oder
viele Leute haben halt dann GIT nur zum
Push oder Pullen verwendet irgendwann.
Man kann echt tolle Sachen mitmachen.
Also spannende Sachen.
Deswegen ist es interessant, um so ein bisschen
Advanced dann auch zu machen.
Und geht ja auch in die gleiche Richtung. Ist ja spannend.
Ähnliche Picks haben die so.
Ja, hat auch was mit Git zu tun.
Lustigerweise.
Ja, auch bei dem
Backtail-Media-Pull-Request
ist mir das dann untergekommen,
weil
ich hatte halt gar nicht
gesehen, dass da irgendwelche
und normalerweise hat man immer so Linting-Steps oder so,
wo man jetzt irgendwie Software testet oder so.
Das war da jetzt gar nicht.
Da dachte ich mir, das ist schon mal ein bisschen komisch.
Und dann hat irgendwie bei GitHub,
hat dann irgendwie das Ding mich angemerkt,
hat so, dein Code ist ja irgendwie,
da sind ja die ganzen Linting-Geschichten nicht richtig
und da ist noch was falsch und es ist falsch.
Du warst schon überrascht, weil du hattest doch gemacht,
was da drin definiert ist.
Genau, und ich dachte schon so, ja gut, so mache ich das halt,
das ist doch normal und das ist doch okay.
Und dann dachte ich so, ja, wie passiert das eigentlich,
das GitHub das weiß, dass das nicht okay ist
und ja, dann wurde ich
auch darauf hingewiesen, so, ja
wir benutzen hier Precommit
benutzen das auch mal auch
und das ist auch ein ganz
das ist glaube ich auch noch ein Projekt von Anthony
Soti, ich weiß gar nicht, wie man den ausspricht
und
man definiert das quasi in so
einer Art Konfigurationsdatei
was man da gerne hätte
und dann sagt man Precommit
Install oder so und dann installiert
es alle Abhängigkeiten, die man braucht, um diese
Geschichten machen zu können lokal,
aber man muss diese Abhängigkeiten dann auch nicht mehr
im Projekt haben. Man hat ja normalerweise auch mal so eine ganze
Reihe von Dingen, ja, was weiß ich nicht,
Black, Flag8, dieses ganze
Zeugs, auch im gleichen Projekt
dann immer rumhängen und...
Das ist Death Dependencies quasi, Herr Sotz, oder?
Ne, ist nicht Death Dependencies.
Death Dependencies sind ja auch nochmal andere Sachen,
aber man hat halt diesen Kram dann da nicht
mehr drin, sondern das, ich weiß gar nicht,
dass ein eigenes Virtual
Environment installiert wird, ich weiß jetzt gar nicht genau,
wie es dann technisch funktioniert. Auf jeden Fall
ist es dann so, wenn man sagt, dann git commit oder so,
dann läuft das dann halt automatisch
durch und wenn
es nicht
richtig war, dann sagt es irgendwie, nee, kann ich
committen, weil da ist halt zum Beispiel irgendwie noch
ein, da ist ein
Plague 8 hat mir gesagt, da ist was faul oder
es sortiert hier halt einfach mal die Imports um mit
iSort oder es macht halt irgendwie
Man kann dann eigene Regeln anlegen, also jedes Mal muss man
über Droba was machen, ja okay. Genau.
Der Dockstring ist nicht lang genug.
Ja, also das
kannst du halt konfigurieren, wie du willst, aber
Es wird dann halt auch so mal
ein Comment gemacht.
Das ist natürlich auch ein bisschen hart.
Ja, klar.
Das ist ja schon mal super.
Das kann man ja relativ leicht hinfaken.
Ja, genau.
Aber das fällt ja schon mal auf, wenn er nicht da ist, zum Beispiel.
Ja, es ist natürlich relativ hart, aber es ist ja
eigentlich schon auch der richtige Zeitpunkt.
Du kannst nicht committen, ohne dass alles in Ordnung ist.
Genau, das heißt, es ist damit sichergestellt,
dass nicht irgendwelcher Quatsch da drin ist.
Wahrscheinlich kann man das dann auch irgendwie umgehen.
Ist das eine Library oder was?
oder ist das...
Ja, das ist ein Projekt, das ist auch ein Kommando-Zeilentool.
Man benutzt es als
Kommando-Zeilentool und man sagt im Grunde einmal
pre-commit install und dann guckt es halt in diese
Konfigurationsdateien, macht seine Magie
und danach sind diese Git-Hooks
gesetzt.
Das muss man quasi bei jedem Dev erstmal einmal installieren, damit das funktioniert.
Ja, okay.
Spannend.
Ja, fand ich auch gut.
Dann sind wir heute durch mit dem Thema
und vielen Dank, dass ihr uns wieder dazugehört habt.
Und egal wo ihr seid, morgens, mittags, abends, nachts
und bleibt uns gewogen, hört uns gerne
wieder, schaltet wieder rein und euch noch einen schönen
was auch immer Tag, Nacht,
bis demnächst.
Alles klar, bis demnächst.