Funktionen 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
zappa
User
Beiträge: 26
Registriert: Samstag 19. März 2011, 22:31

Hi habe zwei Dateien:

die Datei body.py

Code: Alles auswählen

def bmi():
    gewicht = int( gewicht_entry.get())
    groesse = int(groesse_entry.get())
    bodyMass = gewicht * groesse #ich weiß, das ist quatsch
und die Datei rechner.py

Code: Alles auswählen

import body
import tkinter as tk


main = tk.Tk()

gewichtL = tk.LabelFrame(main, text = "Gewicht")
gewichtL.pack()
gewicht_entry = tk.Entry(gewichtL)
gewicht_entry.pack()

groesseL = tk.LabelFrame(main, text = "Größe")
groesseL.pack()
groesse_entry = tk.Entry(groesseL)
groesse_entry.pack()

button = tk.Button(main, text = "BMI", command = body.bmi)
button.pack()



main.mainloop()
In rechner.py importiere ich body und übergebe dem button das Kommando body.bmi.
Jetzt bekomme ich die Fehlermeldung
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python33\lib\tkinter\__init__.py", line 1442, in __call__
return self.func(*args)
File "C:/Python33\body.py", line 5, in bmi
gewicht = gewicht_entry.get()
NameError: global name 'gewicht_entry' is not defined

... aber warum. Ich führe doch body.bmi in rechner aus, da müsste doch gewicht_entry erkannt werden???
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Hallo zappa,

1) globale Variablen sind erstmal sowieso schlecht.
2) jedes Modul hat seine eigenen »globalen« Variablen
3) warum übergibst Du nicht gewicht und groesse als Parameter?
4) warum gibt bmi keinen Wert zurück, sondern rechnet nur, und schmeißt das Ergebnis weg?
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Sirius3 hat geschrieben:4) warum gibt bmi keinen Wert zurück, sondern rechnet nur, und schmeißt das Ergebnis weg?
Es sieht so aus, als sei dem Fragesteller das Konzept der Namensräume noch nicht wirklich klar.

Wenn also dazu Fragen sind: her damit.
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Sirius3 hat geschrieben:4) warum gibt bmi keinen Wert zurück, sondern rechnet nur, und schmeißt das Ergebnis weg?
Na, das hat zappa doch selber in seinem Code in einem Kommentar geschrieben:
zappa hat geschrieben:

Code: Alles auswählen

bodyMass = gewicht * groesse #ich weiß, das ist quatsch
Damit zeigt er doch, dass er selber nicht viel von der BMI-Formel hält (die ist nämlich "quatsch") und die errechneten Werte daher lieber schnell vergessen möchte :P
zappa
User
Beiträge: 26
Registriert: Samstag 19. März 2011, 22:31

:cry:

Stimmt - mit den Namensräumen das ist so ne Sache:
Also ich hab noch nen Versuch gestartet - hier klappt es auch:
Das ist die body.py

Code: Alles auswählen

class Test:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def drucke(self):
        print("Ich bin die 'body.py'. Mein Name ist: {0}, Alter ist {1}".format(self.name, self.age))
              

test = Test("Tom", 15)
... und das die zweite Datei

Code: Alles auswählen

import body

class Neu:
    def __init__ (self, ort, kosten):
        self.ort = ort
        self.kosten = kosten
    def drucken(self):
        print ("Die Stadt ist {0}, die Kosten sind {1} Euro".format(self.ort, self.kosten))
    def holeBody(self):
        body.test.drucke()

    


neu = Neu("München", 2000)
neu.drucken()
neu.holeBody()
Warum geht es hier ? und in dem anderen Bsp. nicht?
BlackJack

@zappa: Im ersten Beispiel importierst Du ein Modul und hoffst, dass das *importierte* Modul auf magische Weise Namen im importierenden Modul kennt.

Im zweiten Beispiel importierst Du ein Modul und greifst ganz explizit auf ein Attribut von dem importierten Modul zu.

Ein ``import`` ändert nichts an dem importierten Modul (ausser natürlich beim ersten Import, dass der Inhalt des Moduls ausgeführt wird). Man beeinflusst damit nur den Namensraum des importierenden Moduls, in dem man dort entweder das Modul an einen Namen binden, oder Attribute aus dem importierten Modul auch im importierenden Modul an Namen bindet.

Bevor Du jetzt auf die Idee kommst das erste Beispiel so zu erweitern, dass das `body`-Modul zusätzlich das `rechner`-Modul importiert: Lass es! Wenn sich Module gegenseitig importieren, dann wird das ganze noch verwirrender und funktioniert in aller Regel nicht so wie man das gerne hätte.

Die Lösung wurde ja schon genannt: Die Funktion im `body`-Modul sollte nichts vom `rechner`-Modul wissen. Die Werte die dort verwendet werden übergibt man als Argumente. Dann kann man die `bmi()`-Funktion auch wiederverwenden. Wenn Module sich gegenseitig tatsächlich *brauchen*, dann stellt sich automatisch die Frage warum das überhaupt auf Module aufgetrennt wurde und nicht zusammen in einem Modul steht. Denn dann gehört das offenbar eng zusammen, und zu Gruppieren von Sachen die eng zusammen gehören sind Module ja da.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

zappa hat geschrieben:Warum geht es hier ?
Weil Du es hier richtig machst... :)
  • Im Modul 'body' wird auf Modulebene (erreichbar über modulname.name) ein Exemplar der Klasse 'Test' an den Namen 'test' gebunden.
  • Das zweite Modul importiert nun das 'body' Modul. Somit erhältst Du auf alles, was sich im 'body' Modul befindet über die "Vorwahl" 'body' Zugriff. Dass es sich im weiteren bei 'body' um eine Vorwahl handelt, erkennt Python daran, dass auf den Namen ein Punkt folgt.
  • Da Python von außen nach innen sucht, passiert beim Aufruf von 'neu.holeBody' folgendes:
  • Zuerst wird innerhalb des Namensraumes der Klasse 'Neu' nach dem Namen 'body' gesucht.
  • Nachdem dieser nicht gefunden wird, sucht Python außerhalb, in diesem Falle also auf Modulebene.
  • Dort wird 'body' gefunden. Der Punkt hinter 'body' signalisiert, dass nicht der Name direkt gemeint ist, sondern erstmal ein anderer Namensraum betreten werden soll ("Vorwahl"!).
  • Nachdem also über 'body.' der Namensraum des Moduls 'body' betreten wurde, wird dort nach 'test' gesucht und direkt auf Modulebene gefunden.
  • Da sich auch hinter 'test' ein Punkt befindet wird in dessen Namensraum nach dem Namen 'drucke' gesucht. Zur Orientierung: An den Namen 'test' wurde ein Exemplar der Klasse 'Test' gebunden. Wir befinden uns also jetzt innerhalb des Namensraumes der Klasse 'Test'.
  • Dort wird der Name 'drucke' gefunden und aufgerufen.
Hättest Du in Deinem ersten Beispiel im Modul 'body' das Modul 'rechner' importiert und innerhalb der Funktion 'bmi' statt 'gewicht_entry' (der Name existiert ja weder innerhalb auf Funktionsebene noch außerhalb auf Modulebene, darum die Fehlermeldung) sondern dann 'rechner.gewicht_entry' aufgerufen, wäre alles rosa gewesen!

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
BlackJack

@mutetella: Es wird nie im Namensraum der Klasse `Neu` nach `body` gesucht. In Namensräumen von Klassen wird nur automatisch gesucht, während die ``class``-Anweisung ausgeführt wird, sonst muss man über das Klassenobjekt explizit auf Attribute der Klasse zugreifen. Was Du wohl meintest: Zuerst wird in den lokalen Namen der Methode `holeBody()` gesucht, bevor dann als nächstes auf Modulebene nachgeschaut wird.

Zum Schluss wird es bei der Aufzählung etwas ungenau, denn man führt ja nicht die ungebundene Methode `Test.drucke()` direkt aus, sondern bekommt vom `test`-Exemplar eine an dieses Exemplar gebundene Methode, die ihrerseits beim Aufruf dann die `Test.drucke()`-Funktion/Methode aufruft.

Vielleicht sollte man auch noch erwähnen, das im Zusammenhang mit dem Punktoperator diese ganzen „Namensraum” und „betreten” Formulierungen eine andere Sprechweise für Attribut-Zugriffe sind. Und zwar eine die zwar die Sachverhalte erklärt, aber in der der Punktoperator nicht wirklich gut erklärt wird. Der Punktoperator ist ein binärer Operator wie ``+``, ``-``, ``*``, und so weiter, mit der Besonderheit, das auf der rechten Seite ein Name stehen muss und dieser *Name* bei der Operation verwendet wird und nicht das Objekt was hinter dem Namen steckt. Der Ausdruck ``body .`` macht gar nichts, denn er ist nicht vollständig. Es fehlt der Operand auf der rechten Seite. Erst der Ausdruck ``body . test`` kann ausgewertet werden und bedeutet „nimm das Objekt hinter dem Namen `body` und frage es nach einem Attribut mit dem Namen `test`.”
Antworten