Seite 1 von 1
Verrückter Configparser
Verfasst: Dienstag 3. März 2015, 03:57
von Sophus
Ich mache mich gerade daran, zu überprüfen, ob eine bestimmte Datei existiert. Hier in diesem Fall geht es um die INI-Datei namens config. Die Überprüfung scheint zu klappen. Wenn die Datei nicht existiert, soll die
handle_menu_language-Funktion aufgerufen werden. Hier soll der Anwender seine Sprache auswählen. Zur Zeit steht nur Englisch oder Deutsch zur Verfügung. Nach der Auswahl der Sprache wird die
set_language-Funktion aufgerufen. Wie ihr anhand der Zeile 37 und 38 seht, wird auf jeden Fall die
set_language-Funktion aufgerufen. Nur möchte ich über das Argumente die entsprechende Sprache übermitteln. Die entsprechende Sprache soll dann im Argument
prompt landen (Zeile 26). Und prompt soll dann in Zeile 30 "hinzugefügt" und und in die INI-Datei abgespeichert werden.
Code: Alles auswählen
import configparser
import os
""" Konstante """
INI_FILENAME = "config"
FILE_EXTENSION ="ini"
""" init vars """
separator = os.sep
newline = os.linesep
configFile = os.getcwd() + separator + INI_FILENAME + "." + FILE_EXTENSION
config = configparser.ConfigParser()
def handle_menu_language(choose_language_menu):
while True:
for index, item in enumerate(choose_language_menu, 1):
print("{} {}".format(index, item[0]))
print ""
choice = int(input("Select a language: ")) - 1
if 0 <= choice < len(choose_language_menu):
choose_language_menu[choice][1]()
else:
print("Please only numbers between 1 - {}.".format(
len(choose_language_menu)))
def set_language(prompt):
with open(configFile, 'w') as f:
f.write("[Language_Configuration]\n")
f.write("\n")
f.write("language=" + prompt)
f.close()
def check_configfile_exists():
if not os.path.isfile(configFile) or not os.access(configFile, os.R_OK):
print("**\n\tConfig-file does not exist.\n**")
choose_language_menu = [
["German", set_language("German")],
["English", set_language("English")],
["Quit", quit]
]
handle_menu_language(choose_language_menu)
else:
print("**\n\tConfig-file does exist.\n**")
def main():
check_configfile_exists()
if __name__ == "__main__":
main()
Jedoch bekomme ich folgende Fehlermeldungen:
Traceback (most recent call last):
File "D:/Dan/Python/�bung/Calc/start.py", line 185, in <module>
main()
File "D:/Dan/Python/�bung/Calc/start.py", line 166, in main
check_configfile_exists()
File "D:\Dan\Python\�bung\Calc\config.py", line 41, in check_configfile_exists
handle_menu_language(choose_language_menu)
File "D:\Dan\Python\�bung\Calc\config.py", line 22, in handle_menu_language
choose_language_menu[choice][1]()
TypeError: 'NoneType' object is not callable
Und komischer weise legt das Skript trotz Fehlermeldung die Datei config.ini an, jedoch sieht der Inhalt der INI-Datei wie folgt aus:
[Language_Configuration]
language=English
Außerdem muss ich noch hinzufügen, das es egal ist, welche Sprache ich wähle. Ich kann German oder English wählen , der Inhalt bleibt immer gleich. Ich hätte auch für jede Sprache eine Funktion schreiben können, jedoch wären die Funktionen alle gleich, nur das der Eintrag der jeweiligen Sprache sich variiert hätte. Aus diesem Grund zog ich es vor, das Problem über das Argument zu lösen. Nur leider "spinnt" da meint Skript etwas. Hat jemand eine Ahnung warum?
Re: Verrückter Configparser
Verfasst: Dienstag 3. März 2015, 06:49
von pillmuncher
@Sophus: Versuch doch mal auf Deutsch zu erklären, was die Zeilen 36-40 machen.
Re: Verrückter Configparser
Verfasst: Dienstag 3. März 2015, 08:33
von snafu
@Sophus: An der Stelle, wo er meckert, versuchst du, den Rückgabewert eines Funktionsaufrufes aufzurufen. Und dieser ist im Falle von `set_language(sprache)` eben `None`. Sehr wahrscheinlich möchtest du aber nur vorab festlegen, was genau aufgerufen werden soll (also welche Funktion mit welchen Parametern). Dazu kann man entweder `functools.partial()` oder `lambda` verwenden. Nähere Informationen dazu findest du über Google bzw in der Python-Doku.
Nebenbei erwähnt solltest du das Setzen der Sprache definitiv nicht manuell erledigen. Auch dafür kann man den `ConfigParser` verwenden.
Zudem ist `prompt` als Parameter an dieser Stelle ein schlecht gewählter Name. Unter einem Prompt versteht man normalerweise eine Eingabeaufforderung. Es bezeichnet also das, was der Anwender auf dem Bildschirm sieht, wenn er etwas eingeben soll. Das Ergebnis der Eingabe wird jedoch nicht Prompt genannt.
Re: Verrückter Configparser
Verfasst: Dienstag 3. März 2015, 09:46
von BlackJack
@Sophus: Anmerkungen zum Quelltext: Pfade setzt man besser mit `os.path.join()` zusammen statt mit ``+``. Auch wenn man dort `os.sep` verwendet ist das nicht das selbe.
Das was auf Modulebene als Variablen bezeichnet wird sind eigentlich bis auf `config` Konstanten. Und `config` sollte dort nicht definiert werden.
Literale Zeichenketten sind nur an bestimmten Stellen als alleinstehende Werte sinnvoll, nämlich da wo sie vom Compiler als Docstrings erkannt werden. Zeichenketten sind keine Kommentare und sollten auch nicht als welche missbraucht werden.
In `handle_menu_language()` kann der Benutzer das Programm durch Eingabe von etwas das keine Zahl ist zum Abbruch zwingen.
Wenn man ``with`` verwendet ist ein `close()` nicht mehr nötig.
Re: Verrückter Configparser
Verfasst: Dienstag 3. März 2015, 11:35
von EyDu
Und der Name "check_configfile_exists" ist wieder irreführend. Diese Funktion sollte genau eine Sache machen: Prüfen, ob eine Konfigurationsdatei existiert. Du lässt aber zusätzlich noch den Benutzer eine Sprache wählen, das ist sehr unerwartet. Bei der Gelegenheit kannst du gleich noch zwei weitere Dinge lernen. Vermische keine Logik mit Eingabe und Ausgabe, und das EAFP-Prinzip. Letzteres besagt, dass du einfach versuchen solltest eine Operation durchzuführen. Wenn das nicht funktioniert, dann wird schon eine Exception geworfen und du kannst dich dann um den Sonderfall kümmern. Gemeint ist hier der Test, ob eine Datei existiert und ob du Zugriffsrechte hast. Da könnten prinzipiell noch viel mehr Dinge Probleme verursachen (es könnte sogar sein, dass die Datei in der Zwischenzeit gar nicht mehr existiert, oder du keine Zugriffsberechtigung mehr hast). Versuche daher einfach die Datei zu öffnen. Wenn es geht, dann ist gut, wenn nicht, dann bekommst du eine Exception und reagierst drauf. Selbiges gilt für die Zeilen 21 bis 24. Nicht vorher fragen ob es geht, einfach machen. Wenn der Schlüssel nicht existiert, dann gibt es einen KeyError.
Re: Verrückter Configparser
Verfasst: Dienstag 3. März 2015, 17:10
von Sophus
pillmuncher hat geschrieben:@Sophus: Versuch doch mal auf Deutsch zu erklären, was die Zeilen 36-40 machen.
snafu hat geschrieben:@Sophus: An der Stelle, wo er meckert, versuchst du, den Rückgabewert eines Funktionsaufrufes aufzurufen. Und dieser ist im Falle von `set_language(sprache)` eben `None`. Sehr wahrscheinlich möchtest du aber nur vorab festlegen, was genau aufgerufen werden soll (also welche Funktion mit welchen Parametern). Dazu kann man entweder `functools.partial()` oder `lambda` verwenden. Nähere Informationen dazu findest du über Google bzw in der Python-Doku.
Hallo pillmuncher, snafu
in den besagten Zeilen (36-40) handelt es sich zunächst um eine Liste namens
choose_language_menu . In diesem Fall handelt es sich sogar um eine Liste von Paaren - Text und Funktionen wurden direkt "zusammen gekoppelt". Bei diese Art von Liste geht die Auswahl der Sprache über den Index. Zu den Funktionen wollte ich einen Parameter übergeben,
ohne einen Rückgabewert zu erhalten. In VB6 würde ich das mittels eine Sub-Prozedur erledigen - einfach einen Wert übergeben, und die Prozedur arbeitet mit den übergebenen Werten. Denn die Sub-Prozedur ist nicht in der Lage einen Wert zurück zu liefern. Da ich nicht weiß, ob dies auch in Python funktioniert, habe ich versucht dies über die Funktion zu lösen, eben ohne
return-Anweisungen. Da habe ich einfach gehofft, dass kein Rückgabewert stattfindet, und die Funktion die Werte einfach annimmt, und damit entsprechend vorgeht.
Re: Verrückter Configparser
Verfasst: Dienstag 3. März 2015, 17:26
von snafu
@Sophus: "Hoffen" ist beim Programmieren nicht gerade die beste Herangehensweise...
Wie du dein Vorhaben korrekt umsetzen kannst, habe ich ja bereits erwähnt.
Re: Verrückter Configparser
Verfasst: Dienstag 3. März 2015, 17:29
von pillmuncher
@Sophus: Das Problem ist, dass du die Funktion
set_language() in den Zeilen 37 und 38
aufrufst. In dem Moment wird sie ausgeführt, und zwar zweimal in genau in der Reihenfolge, die du angegeben hast, also erst mit dem Argument "
German" und danach mit "
English". In den Listen
["German", set_language("German")] und
["English", set_language("English")] steht dann der Rückgabewert des Funktionsaufrufs, und das ist jeweil
None. Woher soll Python auch wissen, dass du an dieser Stelle
möchstest, dass der Aufruf erst später stattfinden soll? Dazu müsste es Gedanken lesen können, und bei allem, was ich Guido van Rossum zutraue, das wird auch er nicht implementieren können. Dave Beazley könnte es vielleicht, aber der ist AFAIK kein Core-Entwickler.
Die übliche Lösung für dieses Problem, ist
lambda zu verwenden:
Code: Alles auswählen
def check_configfile_exists():
if not os.path.isfile(configFile) or not os.access(configFile, os.R_OK):
print("**\n\tConfig-file does not exist.\n**")
choose_language_menu = [
["German", lambda: set_language("German")],
["English", lambda: set_language("English")],
["Quit", quit]
]
handle_menu_language(choose_language_menu)
else:
print("**\n\tConfig-file does exist.\n**")
Ansonsten gilt, was bereits gesagt wurde: von einer Funktion, die
check... heißt, würde ich nicht erwarten, dass sie den Programmzustand ändert, oder gar eine Konfigurationsdatei schreibt.
Re: Verrückter Configparser
Verfasst: Dienstag 3. März 2015, 17:43
von Sophus
BlackJack hat geschrieben:@Sophus: Anmerkungen zum Quelltext: Pfade setzt man besser mit `os.path.join()` zusammen statt mit ``+``. Auch wenn man dort `os.sep` verwendet ist das nicht das selbe.
Meinst du das etwa so?
Code: Alles auswählen
import os
""" Konstante """
BASE_FILENAME = "config"
FILENAME_SUFFIX = ".ini"
configFile = os.path.join(BASE_FILENAME + FILENAME_SUFFIX)
Ich habe jetzt den Code nicht getestet, aber dennoch ist dort ein "+" vorhanden. Ich komme wohl nicht um einen "+" drumherum?
Re: Verrückter Configparser
Verfasst: Dienstag 3. März 2015, 18:29
von pillmuncher
@Sophus: Nein, das hat BlackJack sicher nicht gemeint. Warum trennst du denn überhaupt den Namen
config.ini? Warum nicht einfach:
Code: Alles auswählen
CONFIG_FILE_NAME = os.path.join(getcwd(), 'config.ini')
oder noch besser:
Code: Alles auswählen
def get_module_dir(file_name):
return os.path.dirname(os.path.abspath(file_name))
CONFIG_FILE_NAME = os.path.join(get_module_dir(__file__), 'config.ini')
Dann wird
config.ini nicht im aktuellen Arbeitsverzeichnis gesucht, sondern im Verzeichnis, in dem die Programmdatei liegt. Dadurch ist es möglich, das Programm aus anderen Verzeichnissen aufzurufen, ohne dass in jedem dieser Verzeichnisse eine
config.ini liegen muss.
Re: Verrückter Configparser
Verfasst: Mittwoch 4. März 2015, 18:13
von Sophus
snafu hat geschrieben:@Sophus: "Hoffen" ist beim Programmieren nicht gerade die beste Herangehensweise...
Wie du dein Vorhaben korrekt umsetzen kannst, habe ich ja bereits erwähnt.
Hallo snafu,
ich habe mir deinen Ratschlag sehr wohl zu Herzen genommen, und mich mit Lambda beschäftigt. Manchmal sind Youtube-Videos auch ganz toll. So schaute ich mir dieses Video an:
https://www.youtube.com/watch?v=r1_LNf0z4mw, und dennoch kam ich nicht auf die Idee, wie ich in meinem Falle die lambda einsetzen soll. Die Funktion ist mir auch recht neu. Aber dank pillmuncher sah ich, dass man nur lambda: hinschreiben muss:
"German", lambda: set_language("German")
Ich habe anhand des Videos wohl zu kompliziert gedacht, und mir dann gedacht "Ohhh jeeeeee".
Re: Verrückter Configparser
Verfasst: Mittwoch 4. März 2015, 18:21
von Sophus
pillmuncher hat geschrieben:@Sophus: Nein, das hat BlackJack sicher nicht gemeint. Warum trennst du denn überhaupt den Namen
config.ini? Warum nicht einfach:
Code: Alles auswählen
CONFIG_FILE_NAME = os.path.join(getcwd(), 'config.ini')
oder noch besser:
Code: Alles auswählen
def get_module_dir(file_name):
return os.path.dirname(os.path.abspath(file_name))
CONFIG_FILE_NAME = os.path.join(get_module_dir(__file__), 'config.ini')
Dann wird
config.ini nicht im aktuellen Arbeitsverzeichnis gesucht, sondern im Verzeichnis, in dem die Programmdatei liegt. Dadurch ist es möglich, das Programm aus anderen Verzeichnissen aufzurufen, ohne dass in jedem dieser Verzeichnisse eine
config.ini liegen muss.
Hallo pillmuncher,
besten dank für die Hilfe, auch beim lambda-Problem. Die Funktion gefällt mir viel besser.
