[Python] Cross-module references

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • Stijn
  • Registratie: Februari 2005
  • Laatst online: 13-05 13:23
Ik heb een python-applicatie die grofweg de volgende structuur heeft:

Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
> core.py
from class_a import a
from class_b import b

obj1 = a()
obj2 = b()

> class_a.py
class a:
    def message(self, msg):
        print('message: %s' % msg)

> class_b.py
class b:
    def __init__(self):
        core.obj1.message('object with class b created')

Het daadwerkelijke programma is natuurlijk uitgebreider, maar dit is het basis-idee. Het probleem waar ik tegenaan loop is dat ik vanuit class b blijkbaar niet naar objecten die ik in core heb aangemaakt kan verwijzen. Ik krijg de fout dat "core" geen global name is. Dit is op zich logisch, maar als ik in class_b.py "import core" doe geeft hij begrijpelijkwijs ook een fout, aangezien dat een infinite loop is (core importeert class_b importeert core importeert class_b... etc).

Is er een manier om vanuit een geïmporteerde module te verwijzen naar variabelen/objecten uit de module van waaruit er geïmporteerd wordt?

[ Voor 0% gewijzigd door Stijn op 15-04-2009 22:02 . Reden: verduidelijking ]


Acties:
  • 0 Henk 'm!

  • Bryan Tong Minh
  • Registratie: Juli 2008
  • Laatst online: 18-07 12:49
Run je core.py direct van de command line? In dat geval is core geen module, maar je __main__ en kan bereikt worden via import __main__.

Ik weet niet hoe je class_a gebruikt, maar als je maar 1 instance in je hele programma hebt, zou je class_a's methods ook als static kunnen definieren en dan class_a.a.message() kunnen doen.


Bryan

Acties:
  • 0 Henk 'm!

  • Elijan9
  • Registratie: Februari 2004
  • Laatst online: 20-09 18:03
Je kunt natuurlijk wel met globals werken... Maar dan zul je waarschijnlijk moeten refereren aan
Python:
1
globals()['obj1'].message()
of iets dergelijks.

Maar layout-technisch gezien heeft class_b een dependency op class_a en moet die dus geïmporteerd worden, dat is geen cyclische afhankelijkheid.

Maar volgens mij wil je eigenlijk dat class a gebruikt kan worden als een singleton, dan kun je dat ook beter ook zo programmeren...

War is when the young and stupid are tricked by the old and bitter into killing each other. - Niko Bellic


Acties:
  • 0 Henk 'm!

  • djc
  • Registratie: December 2001
  • Laatst online: 08-09 23:18

djc

mrbartjens schreef op woensdag 15 april 2009 @ 22:01:
Is er een manier om vanuit een geïmporteerde module te verwijzen naar variabelen/objecten uit de module van waaruit er geïmporteerd wordt?
Je kan de import van core pas laten plaats vinden vlak voor je ernaar wilt schrijven. Niet de meest nette oplossing.

Dit soort cyclische dependencies wijzen vaak op een tekortkoming in je architectuur. Als je een singleton wil, doe het dan gewoon zonder object, maar gebruik een module. Als je logging wil, gebruik dan de logging module uit de stdlib.

Rustacean


Acties:
  • 0 Henk 'm!

  • Stijn
  • Registratie: Februari 2005
  • Laatst online: 13-05 13:23
Bryan: Dank je, dat lijkt inderdaad te zijn waar ik naar op zoek was. Ik kom nu wel een ander vreemd probleem tegen: "self" lijkt niet meer automatisch doorgegeven te worden aan methods die ik via deze weg aanroep. Als ik "__main__.obj1.message('iets')" doe, krijgt in a.message "self" de waarde "iets", en blijft "msg" leeg. Hoort dit of doe ik iets fout?

Anderen: De eigenlijke applicatie is een IRC-bot die met meerdere servers tegelijk een verbinding moet onderhouden. Elke verbinding wordt door een apart object afgehandeld. Er is dan nog een losstaande class die door al die objecten gebruikt wordt om binnenkomende IRC-berichten af te handelen. Dat kan op zich een singleton zijn, maar ik wil wel vanuit die afhandel-class terug kunnen verwijzen naar het IRC-object (om ook berichten terug te sturen). Dat gaat in mijn huidige opzet, waarbij ik per class een los bestand heb, en één hoofdbestand waarin de verbindingen worden aangemaakt, niet, omdat ik de verwijzing niet werkend krijg.

Acties:
  • 0 Henk 'm!

  • Elijan9
  • Registratie: Februari 2004
  • Laatst online: 20-09 18:03
Wat je waarschijnlijk zou moeten doen en wat jij eigenlijk bedoelt/wilt volgens mij, is obj1 meegeven aan de init van class b:
Python: class_b.py
1
2
3
4
class b:
    def __init__(self, obj1):
        self.obj1 = obj1
        self.obj1.message('object with class b created')

Python: core.py
5
6
obj1 = a()
obj2 = b(a)

War is when the young and stupid are tricked by the old and bitter into killing each other. - Niko Bellic


Acties:
  • 0 Henk 'm!

  • Sh4wn
  • Registratie: December 2006
  • Laatst online: 12-11-2017

Sh4wn

Bio-informatica

mrbartjens schreef op donderdag 16 april 2009 @ 16:08:
Bryan: Dank je, dat lijkt inderdaad te zijn waar ik naar op zoek was. Ik kom nu wel een ander vreemd probleem tegen: "self" lijkt niet meer automatisch doorgegeven te worden aan methods die ik via deze weg aanroep. Als ik "__main__.obj1.message('iets')" doe, krijgt in a.message "self" de waarde "iets", en blijft "msg" leeg. Hoort dit of doe ik iets fout?

Anderen: De eigenlijke applicatie is een IRC-bot die met meerdere servers tegelijk een verbinding moet onderhouden. Elke verbinding wordt door een apart object afgehandeld. Er is dan nog een losstaande class die door al die objecten gebruikt wordt om binnenkomende IRC-berichten af te handelen. Dat kan op zich een singleton zijn, maar ik wil wel vanuit die afhandel-class terug kunnen verwijzen naar het IRC-object (om ook berichten terug te sturen). Dat gaat in mijn huidige opzet, waarbij ik per class een los bestand heb, en één hoofdbestand waarin de verbindingen worden aangemaakt, niet, omdat ik de verwijzing niet werkend krijg.
Ik ben zelf ook bezig met een tamelijk uitgebreide IRC bot in python ( http://luckybot.googlecode.com ). Ik heb het op de volgende manier aangepakt:

Het begint bij het bestand run.py, deze checked of de optie --no-gui is gezet, en selecteert aan de hand daarvan welke 'View' we nemen. Ik heb dus een console en een GUI view. Daarna wordt mijn hoofd 'Bot' object gemaakt. Ik stel het daarvoor aangemaakte view object in als property van mijn bot object.

Het bot object leest dan alle instellingen uit de ini files, welke plugins er geladen moeten worden e.d. Als dat allemaal gedaan is wordt de view 'gestart'. Op dat moment wordt of het hoofd scherm tevoorschijn getoverd, of de console wordt geinitialiseerd. Bij de console versie wordt direct voor elke server in het instellingen bestand een connectie opgezet. Mijn connection class is gebaseerd op gobject, voor async I/O. Ook kan ik hierdoor makkelijk een event systeem opzetten. Het hoofd bot object 'luistert' naar deze events (het belangrijkste event is als er nieuwe data is, als er een nieuwe regel van IRC server wordt ontvangen, wordt er een functie in mijn hoofd bot object aangeroepen). Het mooie van gobject's event systeem, is dat de verstuurder van het event, ook wordt meegegeven als functie argument aan de ontvanger. Aangezien de connectie class de verstuurder is, heeft mijn hoofd bot object dus ook direct een object naar de connectie van de juiste IRC server. Het bot object roept dan weer de plugin manager aan, en geeft het connectie object door aan de plugin manager en self.

Hierdoor kan de plugin manager kijken of er een plugin is aangeroepen (via het bot object kan de plugin manager bij de instellingen komen, voor bijvoorbeeld de command prefix). Als er een plugin is aangeroepen, geeft de plugin manager het bot object en het connectie object door aan de plugin -> Die kan dingen terugsturen naar de server.

API documentatie: http://www.return1.net/docs/luckybot/index.html
Kan je goed kijken hoe de class structuur er uitziet.

Hoop dat het een beetje duidelijk is zo, want zelf denk ik dat static niet de manier is in python. Je kan sowieso nooit 100% singleton krijgen in python.

Acties:
  • 0 Henk 'm!

  • Stijn
  • Registratie: Februari 2005
  • Laatst online: 13-05 13:23
Sh4wn: Bedankt, dat ziet er interessant uit. Ik denk dat jouw project iets grootschaliger is dan het mijne maar wie weet kan ik er wat nuttige ideeën uit halen :)

Ik ben voorlopig maar voor de oplossing van Elijan9, maar dat zit toch niet helemaal lekker. Zeker niet nu ik ook bijv. een SQLite database will gebruiken en dus moet kiezen tussen in elke class een losse verbinding opzetten (niet echt efficient) of de SQLite-verbinding als init-argument meegeven. Dat laatste gaat nog, maar ik zie het wel gebeuren dat ik ook andere dergelijke dingen wil meegeven en dan krijg je gedrochten als

Python:
1
connection = irc(server, handler, sql, settings, ..., ...)


Maar goed, voorlopig kan ik weer even vooruit, bedankt :)

Acties:
  • 0 Henk 'm!

  • Remus
  • Registratie: Juli 2000
  • Laatst online: 15-08-2021
mrbartjens schreef op vrijdag 17 april 2009 @ 14:07:
Ik ben voorlopig maar voor de oplossing van Elijan9, maar dat zit toch niet helemaal lekker. Zeker niet nu ik ook bijv. een SQLite database will gebruiken en dus moet kiezen tussen in elke class een losse verbinding opzetten (niet echt efficient) of de SQLite-verbinding als init-argument meegeven. Dat laatste gaat nog, maar ik zie het wel gebeuren dat ik ook andere dergelijke dingen wil meegeven en dan krijg je gedrochten als

Python:
1
connection = irc(server, handler, sql, settings, ..., ...)


Maar goed, voorlopig kan ik weer even vooruit, bedankt :)
Een mogelijke oplossing: een aparte module met daarin de database code en die of importeren in de modules die gebruik moeten maken, of runtime relevante functies/methoden injecteren in de diverse modulen/klassen.

Voor een project op werk heb ik ervoor gekozen om een dao module te maken die wordt geïmporteerd bij het opstarten. Als gevolg van de import van de dao module worden de domeinklassen vervolgens geïnjecteerd met de relevante CRUD functies (dit is waarschijnlijk niet de meest handige oplossing, maar voorlopig doet het zijn ding goed en heb ik alle dao code op één plek).
Pagina: 1