Paket-weite Variable

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.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Ich steh gerade mal wieder auf der Leitung. Wie kann ich in Python eine Paket-weite(also für ein gesammtes Paket verfügbare) Variable erstellen. Jetzt könnte ich das natürlich mit relativ Importen machen, aber meine Variable ist keine Konstante sondern ein Objekt. Diese wird einmal in der "main" initialisiert und sollte der Variable zugewiesen werden, danach wird das Objekt nicht mehr geändert.

Ist das jetzt so ein Fall wo man doch mal "global" nutzen sollte ?
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Binde das Objekt doch einfach an ein Modul.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Also einfach eine leere* Instanz auf Modul ebene erzeugen und dann in der Main die Werte setzen. (Welcher Namenskonvention untersteht solch eine Instanz?)

*leer - nur mit default-Parametern gefüttert.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ich stehe hier mal wieder auf dem Schlauch. Wo liegt das Problem, dass die Variable (besser wäre ja: der Name / das Attribut) ein Objekt ist? Wenn sie sich nicht mehr verändert, dann verhält sie sich doch im Prinzip wie eine Konstante. Jetzt mal davon abgesehen, dass in Python bekanntlich alles ein Objekt ist. Ich vermute aber mal, du meintest das eher im Sinne einer Klasse, die noch initialisiert werden muss, oder?
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Ist mir natürlich klar das alles in Python ein Objekt ist, also ja ich meinte eine Instanz eines Klassenobjektes. Ich war mir nur nicht sicher ob man diese einfach auf die Modulebene packen kann, wenn dann mehrere andere Module darauf zugreifen und eventuell Werte verändern. Die Instanz bleibt die gleiche aber nicht unbedingt die Werte darin.

Eigentlich könnte ich ja ein Borg-Pattern nehmen, aber ich möchte von dem Klassenobjekt auch noch andere Instanzen bilden können.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Xynon1 hat geschrieben:Also einfach eine leere* Instanz auf Modul ebene erzeugen und dann in der Main die Werte setzen. (Welcher Namenskonvention untersteht solch eine Instanz?)
Ja, oder einfach das Modulobjekt schnappen und da dann die Klasse dranbinden. Musst halt aufpassen um keine Race-Conditions (AttributeError anyone?) zu produzieren.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Oder einfach mal den konkreten Anwendungsfall nennen, damit klar wird, was du denn nun genau vorhast. So wie ich es bisher verstanden habe, willst du einersetis ein unveränderliches Exemplar haben, auf welches deine Untermodule gemeinsamen Zugriff haben, aber eben nichts verändern dürfen. Auf der anderen Seite benötigst du von dem Objekt zusätzlich noch diverse Exemplare, die an anderer Stelle verwendet werden, richtig? Du könntest doch dann einfach eine veränderbare / "normale" Klasse implementieren und für den Read-Only-Zugriff entsprechend ableiten.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

@Leonidas
Ok, dann mach ich das so, bleibt nur noch die Frage offen "Welcher Namenskonvention untersteht solch eine Instanz?".
Ich meine sollte man auf Modul-Ebene ein solches Objekt besser als normale Instanz, also "klein_mit_unterstrichen" schreiben oder wie ein Konstante handhaben, also "GROS_MIT_UNTERSTRICHEN". - Ich nehme mal eher an mit kleinen Buchstaben oder?, denn es ist ja immer noch eine Instanz.

@snafu
Konkreter Anwendungsfall:
Ich habe ein Klassenobjekt "Spieler", welches Positionsdaten und Bewegungsmethoden beinhaltet. Nun habe ich aber viele Spieler auf einer "Karte" und man selbst ist nur einer von diesen. Was du sehen kannst ist abhängig von dem Blickwinkel deines Spielers bzw. die Distanz von diesem zu den anderen. Also sind viele Methode die mit dem Spieler von der Programmseite her überhaupt nichts zutun haben, wie zB. das Zeichnen der anderen Spieler von deinem eigenen abhängig. Es wurde dann recht lästig aus einem Haufen Spieler immer den eigenen Spieler zu fischen bzw. diesen immer mit an verschiedene Funktionen weiterreichen zu müssen. Also wollte ich eine einzelne Instanz für das gesamte Paket ohne irgendwelche weitere Vererbungen.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
lunar

@Xynon1: De facto arbeitest Du dann mit einer globalen Variable, und Dein ganzes Programm hängt an einem globale Zustand. Genau solche globalen Zustände aber sollte man möglichst vermeiden, denn Programme mit globalen Zuständen sind schlecht zu testen und in geringerem Maße wiederverwendbar.

Wenn es Dir lästig ist, den aktuellen Spieler immer an andere Funktionen weiterzugeben, dann überlege Dir etwas besseres als eine globale Variable.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Wie wäre es mit einer Liste von Spielern, wobei dem aktuellen Spieler ein entsprechendes Flag verpasst wird? Eine Wrapper-Funktion würde dann vor Ausgabe der Daten prüfen, ob diese nach außen hin sichtbar sein sollen, indem es nachsieht, wie das Flag gesetzt ist.
BlackJack

@Xynon1: Kann man den nicht irgendwo anders als "global" unterbringen? Gibt es kein Objekt welches das Spiel modelliert? Da wo die anderen Spieler auch alle in einer Datenstruktur stecken? Da könnte man doch auch die eigene Spielfigur unterbringen.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

@snafu
So ist es ja bis jetzt, das meinte ich mit dem fischen, wobei ich hier eine Wörterbuch benutze um einen direkten Zugriff auf den Spieler zu bekommen.

@lunar
Das wäre nur für das eine Paket und der Spieler wird in fast jeder Funktion gebraucht, wäre es da wirklich so schlimm das so zu machen? Ich meine es ist mir ja nicht "lästig" geworden den immer mitzuliefern, ich finde es nur ziemlich überflüssig.

@BlackJack
Kurze Aufbaubeschreibung:
Es gibt in der Logik einen Dungeon, welcher eine Map besitzt und eine beliebige Anzahl Spieler. In der Grafik-Ebene gibt es ein DungeonSprite welches für das erstellen der Map verantwortlich ist. Zudem werden die Spieler durch ein AnimiertesSprite darstellt. Diese sind aber voneinander getrennt und brauchen dennoch beide den lokalen Spieler. Ebendso die Steuerung oder die (noch nicht eingebaute) Ereignissteuerung. Dann gibt es noch ein Kamera-Objekt, damit der Spieler später eventuell nicht immer zentriert im Bild läuft. Diese sollte aber bei der Bewegung immer auf den lokalen Spieler gerichtet sein und braucht dessen Positionen. Viele Objekte brauchen den lokale Spieler, warscheinlich noch ein ganze Anzahl mehr als ich bisher habe.

Und wenn ich den Spieler wie vorgeschlagen in eine Sammlung packe brauche ich doch auch immer ein "globale" Konstante die mir sagt welches der lokale Spieler ist, oder sehe ich das falsch?
Bei der Liste mit Flag müsste ich ja die Flag kennen, bei meinem jetzigen brauche ich den Namen. Andernfalls muss ich den Spieler ja von der Erzeugung her an jedes Objekt weitergeben und könnte ihn nie, nur in einer Liste ablegen. In wiefern unterscheidet sich das nun von einem Paket-weite(globalen) Objekt?
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
BlackJack

@Xynon1: Viele Objekte beobachten den lokalen Spieler? Wie wäre es mit dem Beobachter-Entwurfsmuster?

Wenn Du den Spieler an die Objekte übergibst, die ihn brauchen, müssen die nicht auf einen globalen Zustand zugreifen. Mir scheint Dein Modul und die Funktionen oder sogar das Paket mit den Funktionen in den Modulen sind so etwas wie ein grosses Singleton-Objekt wenn die alle einen globalen Zustand benötigen. Vielleicht sind die Funktionen, denen Du sonst immer den lokalen Spieler übergeben musst, eigentlich Methoden auf einem ”Spiel”-Objekt!?

Unterschied ist zum Beispiel die isolierte Testbarkeit. Zum Beispiel das ”Kamera”-Objekt kann man einfacher testen wenn man ihm den Spieler übergeben kann, als wenn man die komplette globale Umgebung so herrichten muss, dass der Spieler irgendwo als globaler Wert gefunden werden kann.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Ja, ich merke auch langsam (aber sicher :D) das ich das nochmal überarbeiten müsste.

Problemzone sind halt die Map und der Spieler da dort einige Daten, wie die Position oder die Tile-Größe häufig von grafischen Funktionen benötigt werden. Allerdings will ich diese Werte auch nicht kopieren, da ich sonst immer darauf aufpassen müsste das diese überall noch stimmen.
(Auch die Größe der Tiles kann von Map zu Map unterschiedlich sein, das werde ich warscheinlich balb rauswerfen und konsequent durch 32 ersetzen.)
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Dies bezüglich doch noch eine Frage, wie sollte man dann wirkliche Singletons/Borgs behandeln? Also ich denke da jetzt mal an Resourcen/Event/Effect/Text-Manager, [Chat]Logger, Config oder die Spielumgebung an sich.

Ich meine im Prinzip instanziert man diese alle nur einmal und dann werden sie häufig irgendwo mal gebraucht, wie baut man denn solche Konstrukte ohne die getrennte Testbarkeit zu gefährden :| ? Oder sollte man sie gleich ganz vermeiden?
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ich verstehe dein Problem nicht. Wenn du ein Singleton nutzen möchtest, dann implementiere halt eines. Mir wäre neu, dass sinnvoll genutzte Singletons irgendwie schlecht in Python sind. Beim Testen könntest du halt auf Identität (`is`) prüfen. Ich würde vom Gefühl her (d.h. ohne Details zu kennen) aber nach wie vor eine Liste von Spielern empfehlen, wobei der aktuelle Spieler mit einem Flag gekennzeichnet wird. Ich würde aber sagen: Mach erstmal wie du meinst und später stellst du es eventuell um. Wichtig ist ja in dem Moment, dass es erstmal läuft und man von alleine auf etwaige Unstimmigkeiten stößt, wenn die Klasse halt in der Praxis angewendet wird. Eventuell bist du auch zufrieden mit deiner Lösung - wer weiß (auch wenn mir ein als Singleton implementierter aktueller Spieler äußerst komisch vorkommt). Wäre ja nicht das erste Mal, dass ich mit meinen Ideen / Einwänden auf die Nase falle (aber damit lebe ich gerne, solange ich einen Lerneffekt dabei habe). ;)
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

@snafu
Um den Spieler geht es mir gar nicht mehr, der ist jetzt bei den anderen eingeordnet und es gibt ein Beobachter-Objekt, wie BlackJack es vorgeschlagen hatte.
Das Singleton/Borg(S/B) zu implementieren ist auch nicht das Problem, sondern wie man damit dann arbeiten kann. Denn wenn du eines dieser Muster in einem Klassenobjekt benutzt macht es dieses ja abhängig vom Zustand des S/B. Zum testen müsste ich dann immer den Zustand des S/B [wiederh]erstellen. Bei dem Logger ist das kein Problem, aber bei den Managern ist das nicht gerade einfach.

Deswegen stehe ich wiedermal auf dem Schlauch :? Doch besser gleich prozedural programmieren :mrgreen:
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Na, die Implementierung eines Singleton an sich besteht aus maximal 3 Zeilen und dürfte hinlänglich bekannt / zu ersuchen sein. Ich sag ja: Probier es in der Praxis aus. Als ordentlicher Programmierer willst du bestimmt einen neuen Zweig dafür erschaffen. ;)

Eventuell reden wir aber auch mal wieder aneinander vorbei. Ein Manager darf in meinen Augen durchaus theoretisch mehrere Exemplare haben und muss eigentlich nicht explizit als Singleton implementiert sein. Mehrere Manager benutzen will aber vermutlich eh niemand. Ist halt irgendwo die Gratwanderung zwischen dem Einbeziehen aller möglichen Eventualitäten und dem Aufblähen von Code. Überleg dir halt mal, wie deine Funktionen und Klassen wohl üblicherweise genutzt werden bzw wie wahrscheinlich es ist, dass jemand mehrere Manager benutzen möchte oder (noch wichtiger) inwieweit man etwas kaputt machen könnte, wenn man mehrere Manager-Exemplare benutzt. Sofern du merkst, dass es im Prinzip egal ist, würde ich halt auch das Singleton weglassen.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Na ja, rein theoretisch braucht man S/Bs eigentlich nie, man kann ja auch einfach eine einzelne Instanz auf Modulebene platzieren und dann immer diese nutzen. Die Nutzung dessen ist genau gleich, nur könnte man dann halt weitere Instanzen erzeugen.

Ja, Manager könnten mehrfach instanziert und genutzt werden, hier wäre aber die Frage nach dem Sinn. Z.B. der Resourcenmanger beinhalten das Pfad-Prefix für die Resourcen und parst die Konfigurationsdateien für die Resourcen und beinhaltet dann die Tilesets. All das muss nur einmal geladen werden, wenn man ihn mehrmals instanzieren würde müsste man die Resourcen auch wieder einlesen. Man könnte die Resourcen auch als *statische* Variablen einbauen, aber da man das dann mit allen machen müsste, kann man auch wieder ein S/B nehmen.

Nochmal als Erklärung für die jenigen den es noch nicht klar ist was die Manager machen. Es gibt einige XML-Dateien, welche die Daten bereitstellen z.B. Pfade zu Bildern-, Ton- oder anderen XML-Dateien und Daten wie Id, Name, Gewicht und anderes. Diese werden von den Managern ausgelesen und weiter verarbeitet, wird zum Beispiel ein Bild benötigt erstellt es der Resourcen-Manager zu dem Zeitpunkt an dem es benötigt wird und speichert es. Wird es nochmal benötigt weist er einfach nur auf das Bild, sollte dies geändert werden erstellt er es neu. Bilder können aus mehreren Ebenen bestehen.
Also habe ich hier einen Denkfehler? Denn im Endeffekt mache ich so doch alle Objekte die Resourcen benötigen von dem Zustand des Managers abhängig.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Das, was du beschreibst, klingt für mich eher danach, als willst du einen Cache implementieren, halt um einmal geladene Daten wiederverwenden zu können (meinetwegen auch mittels wie auch immer geartetem Zugriff über einen Manager). Dafür braucht es aber keine "S/B-Implementierung", sondern hier genügt in meinen Augen ein simples Dictionary, welches entweder alle benötigten Daten bei Programmstart lädt oder aber diese Daten auf Anfrage einliest, nachdem erkannt wurde, dass noch kein entsprechender Schlüssel existiert. Aber gut, ich dürfte nun zu Genüge geäußert haben, dass ich bei diesem Anwendungsfall nichts von Borgs halte. Vielleicht kommen ja auch noch ein paar Meinungen von anderen Leuten... ;)
Antworten