[3.3] Pakete mit mehreren Implementierungen importieren

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
lincore
User
Beiträge: 2
Registriert: Montag 10. Februar 2014, 14:38

Moin,

für die Terminalausgabe würde ich gerne zwei Implementierungen anbieten, die das gleiche interface haben und möglichst transparent als Pakete importiert werden sollen. Clients sollen am besten gar nicht merken, dass es mehr als ein Paket gibt. Leider habe ich damit ziemliche Probleme gehabt, die ich teilweise noch nicht einmal verstehe. Mein erster, naiver Versuch sah so aus:

Code: Alles auswählen

in main.py:
  import terminal
  terminal.init(80, 25)
  terminal.puts("Hello, Terminal!")

in terminal/__init__.py:
  # import the actual terminal implementation into this package:
  import config
  if config.backend.lower() == "curses":
    from terminal.ncurses import *
  else:
    from terminal.sdl import *
Ich weiß, import * ist tabu. Ich dachte, ich könne die Implementierung so ganz elegant paketintern lösen und die Unterpakete einfach als '"terminal" selbst ausgeben. Daraus ergaben sich aber zu viele Probleme, vor allem wusste ich irgendwann gar nicht mehr unter welchem Namen, in welchem Kontext, ein Modul jetzt anzusprechen ist. Deshalb frage ich diesmal lieber vorher, wie es richtig geht, ehe ich meinen zweiten Versuch starte.

Wie wäre das hier?

Code: Alles auswählen

:
in getterm.py:
  import config
  if config.backend.lower() == "curses":
    import term_impl.ncurses as terminal
  else:
    import term_impl.sdl as terminal

in client.py:
  from getterm import terminal # ist das okay so? sieht suboptimal aus.
  terminal.init(80, 25)
  terminal.puts("Hello, Terminal!")


Clients könnten das Paket so als terminal ansprechen ohne sich um die Implementierung zu kümmern, während im Paket selbst Module einander entweder explizit relativ oder absolut mit import term_impl.ncurses.foo as foo importieren können, ohne dass es zu Konflikten kommt, oder?

Danke für jede Entwirrung.

PS: Ich komme von Java, vielleicht erklärt das meine Probleme mit import ;~)
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Der zweite Ansatz sieht doch ganz gut aus.

Was den Sternchen-Import angeht: Ich verwende sowas manchmal auch bei Fremdpaketen / -modulen zum Testen in der Python-Shell aus Faulheit. In Produktivcode würde ich aber davon abraten. Ein paar der damit einhergehenden Probleme hast du ja bereits beschrieben.

Was ich wohl manchmal bei eigenen Projekten mache, ist in einer `__init__.py` einige Funktionen und Klassen per Sternchen-Import aus Untermodulen zu importieren, sodass der Anwender diese über das Paket (eben wegen der `__init__.py`) ansprechen kann und daher keine Kenntnis von den Untermodulen benötigt. Das funktioniert aber nur dann gut, wenn der Umfang dieser Elemente überschaubar ist und wenn man in den Untermodulen mittels `__all__` definiert hat, welche Namen sozusagen die öffentliche Schnittstelle darstellen. Nur diese werden dann vom Sternchen-Import in den Namensraum des Pakets geholt. Ohne `__all__` zieht man sich alles aus dem globalen Namensraum des jeweiligen Moduls rüber, was in aller Regel eine ziemlich schlechte Idee ist, weil dann eben auch jegliche dort benutzte Module und Hilfsfunktionen exportiert werden, die den Anwender eigentlich nicht zu interessieren haben.
lincore
User
Beiträge: 2
Registriert: Montag 10. Februar 2014, 14:38

Okay, vielen Dank für die Klarstellung, jetzt muss ich wohl meinen gesamten Code umgraben. Auf zum Refactormobil!
Antworten