Globale Variable vermeiden

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
haidi
User
Beiträge: 20
Registriert: Montag 28. Dezember 2015, 23:03

Hallo,

ich habe eine Liste von 5 Telefonnummern, die ich in diversen Funktionen benötige.
Jetzt habe ich zu Beginn jeder dieser Funktionen dem Variablen doe Telefonnummern zugewiesen, also:

TelAnna="1234"
TelHerbert="45654"
und verwende dann die Variablen.

Wenn ich jetzt wirklcih einmal eine ändern müsste, dann muss ich mich durch den gesamten Code ackern.

Wie kann ich die Telefonnummern nur ein Mal für alle Variablen erfassen?

OK - ich könnte sie in eine Datei schreiben und dann in jeder Funktion einlesen, gefällt mir auch nicht besonders.

Abgesehen davon ist es mir nicht gelungen, eine Variable als global zu definieren.

Code: Alles auswählen

global var
telnr = "a"
drucken()

def drucken()
	print(telnr)
führt zur Fehlermeldung in der function drucken():

Name 'telnr' is not defined
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

haidi hat geschrieben:ich habe eine Liste von 5 Telefonnummern, ...
Stimmt nicht, Du hast jede Telefonnummer an einen separaten Namen gebunden. Eine Liste wäre z. B. sowas:

Code: Alles auswählen

>>> phone_numbers = [('Anna', '1234'), ('Herbert', '45654')]
haidi hat geschrieben:Wie kann ich die Telefonnummern nur ein Mal für alle Variablen erfassen?
So, wie ich das mit der Liste gezeigt habe. Über den Index kannst Du dann darauf zugreifen:

Code: Alles auswählen

>>> phone_numbers[0]
('Anna', '1234')
>>> phone_numbers[1]
('Herbert', '45654')
>>> for phone_number in phone_numbers:
...     print phone_number
... 
('Anna', '1234')
('Herbert', '45654')
Allerdings würde sich zum Ablegen von Telefonnummern ein Dictionary besser eignen:

Code: Alles auswählen

>>> phone_numbers = dict(phone_numbers)
>>> phone_numbers['Anna']
'1234'
>>> phone_numbers['Herbert']
'45654'
>>> for name, phone_number in phone_numbers.iteritems():
...     print 'Name: {}\tNumber: {}'.format(name, phone_number)
... 
Name: Anna      Number: 1234
Name: Herbert   Number: 45654
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@haidi: Konstanten global zu definieren ist kein Problem. Dein Code würde aber melden, dass drucker nicht definiert ist. Wenn Du also angeblich nicht funktionierenden Code zeigst, sollte der also dem tatsächlich ausprobierten entsprechen. "global" solltest Du erst gar nicht verwenden und auf Modulebene hat das auch keine Funktion.

Also, was hast Du probiert und was tut nicht?
haidi
User
Beiträge: 20
Registriert: Montag 28. Dezember 2015, 23:03

Ich hab das an sich korrekt gemacht:

Datei test1.py

Code: Alles auswählen

#!/usr/bin/python3

import test2
global TelNr1
global TelNr2
TelNr1= "123"
TelNr2 = "456"Name 'telnr' is not defined
print(TelNr1,TelNr2)

test2.drucken()
Datei test2.py

Code: Alles auswählen

def drucken():
	print(TelNr1,TelNr2,"subroutine")
	return
Fehlermeldung:
Name 'TelNr1' is not defined


BTW: bei den paar Zeilen vielleicht nicht unbedingt notwendig, aber wie kann ich Code mit Zeilennummern posten?
haidi
User
Beiträge: 20
Registriert: Montag 28. Dezember 2015, 23:03

Ergänzung (Edit-Zeit ist schon abgelaufen):

Die Fehlermeldung kam von der Zeile print(TelNr1,TelNr2,"subroutine"), Datei test2.py
BlackJack

@haidi: Wie schon gesagt wurde hat ``global`` auf Modulebene keinerlei Wirkung. Aber vergiss am besten ganz das es dieses Schlüsselwort gibt. Das löst keine Probleme, das schafft nur welche.

`TelNr1` und `TelNr2` sind in Modul `test1` definiert. `drucken()` sucht aber in `test2` danach. Das ist ja gerade der Sinn von Modulen das nicht alles überall bekannt ist, sondern das man verschiedene Namensräume hat mit denen man Sachen voneinander trennen kann.

Werte (ausser Konstanten) die Funktionen und Methoden betreten, sollten das als Argumente tun. Und gegebenenfalls sollten Ergebnisse als Rückgabewert an den Aufrufer zurückgegeben werden. Wenn die Telefonnummern also keine Konstanten sind, dann übergib sie beim Aufruf.

Sollten es Konstanten sein, dann musst Du die entweder in `test2` definieren, oder in einem zusätzlichen Modul und sie dann von dort in `test1` und `test2` importieren. In `test2` kannst Du `test1` nicht importieren. Also es hindert Dich niemand daran das hinzuschreiben, genau wie man auch ein effektfreies ``global`` auf Modulebene schreiben kann, aber zirkuläre Importe sind kompliziert und schwer richtig hinzubekommen und deshalb sollte man das gar nicht erst anfangen.
haidi
User
Beiträge: 20
Registriert: Montag 28. Dezember 2015, 23:03

Danke, das ist es.
Für mich ist es ein bisschen verquer denken, aber so geht es. Ich hab die Telefonnummern zentral abgelegt und kann im Programm sprechende Variablen (bzw. Funktionen) verwenden.
BlackJack

@haidi: Wobei sich mir noch die Frage stellt ob Telefonnummern tatsächlich Konstanten sind und ob Du wirklich die Namen durchnummeriert hast. Letzteres wäre nämlich ein Warnzeichen das man die eigentlich nicht an individuelle Namen binden sollte, sondern in einer Datenstruktur ablegen sollte. Wahrscheinlich eine Liste.
haidi
User
Beiträge: 20
Registriert: Montag 28. Dezember 2015, 23:03

Zur Erklärung:
Projekt:
An zwei Standorten gibt es jeweils eine GMS-Schaltsteckdose. Mit diesen werden Elektro-Heizungen geschaltet. Diese kleinen Büros werden nur ein Mal in der Woche als Außenstelle verwendet.
Die Steckdosen werden per SMS gesteuert.

Durch den Raspberry wird folgendes gemacht
Die Steckdose wird am Abend vor Benutzung des Büros automatisch überprüft, ob sie ansprechbar ist (also nicht abgesteckt)
2 und 1 STunde vor Dienstbeginn wird die Temperatur abgerufen und je nach Temperatur eingeschaltet.
1/2 STunde nach Dienstbeginn wird die STeckdose automatisch abgedreht.
täglich um 18 Uhr wird ein Abschaltebefehl gesendet (falls jemand aufgedreht hat und abzudrehen vergessen hat.

Die direkten Schaltvorgänge der Beraterinnen (Ort ein/aus/status) werden von ihnen an den Raspberry geSMSt und von diesem in die Steckdosen-spezifischen Befehle umgesetzt und weitergeleitet. Die Antworten an das Absender-Handy weitergeleitet.

Für das Senden und Empfangen der SMS habe ich SMS-Tool installiert.

Es gibt 5 Telefonnummern:
Je eine für eine Steckdose:
TelWien
TelBrunn

Meine Telefonnummer
TelHanneser beiden Heizungen

Die TElefonnummern des Diensthandys:
TelDienst

Die Telefonnummer der Beraterin:
TelBeraterin.

Ich mach es jetzt so, wie du zuletzt geraten:

In dem Modul gibt es:

Code: Alles auswählen

def TelDienst
    return("436501234567")
Die SMS wird mit folgendermaßen erzeugt:

Code: Alles auswählen

	HAdateiname="/var/spool/sms/outgoing/"+time.strftime("%Y.%m.%d-%H:%M:%S",HAlt)
	HAdatei=open(HAdateiname,"w")
	HAdatei.write("To: "+TelDienst()+"\n\n"+HAtext2)
	HAdatei.close()
So brauch ich mich nicht mit Nummern herum ärgern, sondern habe eien selbsterklärende Bezeichnung für die verwendete Telefonnummer.
Ändert sich eine Telefonnummer, muss ich sei nur ein Mal ändern.
Ich könnte die Telefonnummern auch in eine Text-Datei eintragen und dann über eine Funktion auslesen, kommt im Prinzip auf gleiche raus und würde ich nur machen, wenn ich das Projekt kompilierte.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@haidi: Welchen Sinn macht es für eine Konstante eine Funktion zu schreiben?

In Wirklichkeit hast Du aber doch Variablen, die Du an Funktionen übergeben willst:

Code: Alles auswählen

def send_sms(telephone_number, text, date):
    filename = "/var/spool/sms/outgoing/{0:%Y.%m.%d-%H:%M:%S}".format(date)
    while open(filename, "w") as sms:
        sms.write("To: {0}\n\n{1}".format(telephone_number, text)
BlackJack

@haidi: Zu einer Funktion die einen Konstanten Wert zurückgibt habe ich eigentlich nicht geraten, zumindest nicht raten wollen, falls Du das so verstanden hast.

Was Du da beschreibst sind Konstanten oder Konfigurationsdaten, die würde ich entweder als Konstanten in ein Modul schreiben, oder in eine Konfigurationsdatei. Also nicht einfach nur unstrukturierter Text, sondern ein etabliertes Format wie CSV, INI, oder JSON. Für alle drei Formate gibt es Module in der Standardbibliothek.

Abkürzungen und komische Präfixe sollte man vermeiden. Was ist `HA`? Heisst `HAlt` semantisch `H-Alt` oder `HA-lt`, wenn ja was bedeutet das `lt`, oder gar `halt` und das A ist nur versehentlich gross? Namen sollen dem Leser Informationen liefern und nicht zum raten animieren. ;-)

Wenn man Namen fortlaufende Nummern angängt, ist das ein „code smell“. Entweder hat man sich keine Zeit genommen einen vernünftigen Namen zu finden, oder man möchte eigentlich eine Datenstruktur verwenden.

Werden denn alle SMS unterschiedlich erzeugt? Es kommt mir ein wenig komisch vor das man anscheinend für jede Telefonnummer auf's Neue Code schreiben muss und keine Funktion schreiben kann der man die Telefonnummer und den Nachrichtentext übergibt.
haidi
User
Beiträge: 20
Registriert: Montag 28. Dezember 2015, 23:03

@Sirus:
Ich bin Python-Anfänger
1) warum das while, wenn ich nur eine SMS schreiben will.

2) das {0} und {1} in sms.write verstehe ich nicht.


@Blackjack:
Ich hab im nach Konstante im Zusammenhang mit Python gegoogelt. Wenn ich es richtig verstanden habe ist vereinbart, dass Konstanten Variablen sind, die nicht geändert werden sollen und deshalb mit Variablennamen in Großbuchstaben benannt werden.
Das hilft mir aber dann doch auch nichts, weil ich sie nicht in den einzelnen Modulen aufrufen kann.

Ich hab mir für meine Module eine eigene Bibliothek geschrieben: hannes.py
Die Variablen in der Bibliothek fangen alle mit HA an (alte Gewohnheit von früher, wo es echte globale Variable gegeben hat.

Die fortlaufenden Nummern entnimmst du offensichtlich dem Posting mit TelNr1, TelNr2 - das war nur als Beispiel für die Frage zu verstehen, natürlich arbeite ich mit beschreibenden Variablennamen.

Für das Erzeugen der SMS habe ich natürlich auch ein Modul, das wollte ich aber nicht auch noch posten, weil dann der gepostete Code mehr geworden wäre und es auch nicht um das Senden der SMS selbst gegangen ist.

Ich bin bemüht, das "Hauptprogramm" möglichst klein zu halten und so viel wie möglich in Module in meiner Bibliothek auszulagern, weil ich das für übersichticher halte.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@haidi: while sollte with heißen. Was ich eigentlich sagen wollte, ist, dass man nicht tief vergraben in irgendwelchen Modulen Konstanten verwenden sollte, die doch recht speziell sind. Irgendwann kommt eine weitere Telefonnummer hinzu und Du fragst Dich, wo überall Du die anderen verwendet hast. Module sollten wiederverwendbar sein, lass also die Telefonnummern im Hauptprogramm und übergebe sie an Funktionen per Parameter.
BlackJack

@haidi: Das ``while`` sollte ein ``with`` sein.

Für die Platzhalter müsstest Du in der Dokumentation der `format()`-Methode nachlesen.

Konstanten sind in der Regel nicht aufrufbar weil es normalerweise keine Funktionen sind. Aber darauf zugreifen kannst Du doch genau so als wenn Du den Namen nicht komplett gross schreibst.

`hannes.py` ist *ein* Modul und kann keine Bibliothek von mehreren Modulen sein. So ein Namenspräfix macht keinen Sinn wenn doch das Modul schon so einen Präfix darstellt. Das ist unnötiger Ballast ohne Mehrwert.

Das klingt so ein bisschen nach „Je mehr Module um so übersichtlicher“. Mit aufteilen auf mehrere Module würde ich ja erst anfangen wenn die wiederverwendet werden, oder wenn ein Modul wirklich zu gross wird. Und wenn es mehr als ein Modul wird, würde ich auch ganz dringend empfehlen die Module in ein Package zu stecken. Sonst hat man mit jedem weiteren Modul einen weiteren potentiellen Konflikt mit anderen Modulen oder Paketen.
Antworten