Forum: PC-Programmierung Dateinamen mit Nummerierung in Python


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von R. F. (inet_surfer88)


Lesenswert?

Hallo,

ich Programmiere eine Lichtsteuerung für die Modellbahn. Nach und nach 
wird die Steuerung um einzelne Häuser erweitert. Jedes Haus hat in 
meinem Python-Programm eine eigene Datei nach folgendem Muster:
Haus001.py, Haus002.py, Haus005.py, .....
Es kommen nicht alle Zahlen vor, manche Häuser haben keine Beleuchtung 
bekommen aber aus anderen Gründen eine Nummer.

Jedes mal wenn ein neues Haus hinzu kommt kopiere ich einfach die Datei 
in das Verzeichnis und muss dann in der main die Datei importieren und 
die darin enthaltene Funktion aufrufen.

Das ganze würde ich gerne Automatisieren in dem ich prüfe ob eine Datei 
vorhanden ist und wenn ja diese importieren und die Funktion aufrufen.

Ich habe bisher folgendes herausgefunden:
1
from pathlib import Path
2
3
my_file=Path("./Haus.py")
4
5
if my_file.is_file():
6
    import Haus
7
    Haus.ausgabe()

Das funktioniert ohne Probleme. Das Problem ist jetzt, wie kann ich das 
ganze in eine Schleife packen die automatisch die Dateinamen mit der 
Zahlenendung prüft?
In der Zuweisung der Variable "my_file" kann ich einen Schleifenzähler 
an den String "./Haus" anhängen und anschließend noch das .py anbhängen. 
Dann funktioniert die Abfrage. Aber wie kann ich das bei
1
import Haus???.py
und
1
Haus???.funktionsaufruf()
lösen? Hier habe ich bisher keine Möglichkeit gefunden.

Der Sinn des ganzen ist das ich nicht jedes mal die main ändern muss, 
sondern einfach die neue Datei per rsync automatisiert rein kopiere. 
Programmiert wird auf dem PC der auf der NAS speichert, die Steuerung 
läuft auf dem Raspberry welcher nach dem Booten mit rsync die neueste 
Version automatisch von der NAS holt. Der Lerneffekt spielt natürlich 
auch eine Rolle, rsync kopiert ja auch die geänderte main ;-)

von A. S. (rava)


Lesenswert?

du kannst zunächste alle "Haus???.py" suchen, z.B. mittels glob()
https://docs.python.org/3/library/glob.html

dann kannst du die Häuser in einer for-schleife importieren. Dabei hilft 
die importlib
https://www.tutorialspoint.com/How-I-can-dynamically-import-Python-module

Das Tutorial beschreibt auch gleich, wie du string-basiert auf Objekte 
zugreifen kannst. Ich hab das noch nie gemacht, aber probiere mal
globals()[hausname].funktionsaufruf()

von Jemand (Gast)


Lesenswert?

R. F. schrieb:
> Ich habe bisher folgendes herausgefunden:
>
>
1
> from pathlib import Path
2
> 
3
> my_file=Path("./Haus.py")
4
> 
5
> if my_file.is_file():
6
>     import Haus
7
>     Haus.ausgabe()
8
>
>
> Das funktioniert ohne Probleme.

Äh, nein, das tut es nicht, fürchte ich. Das funktioniert nur deswegen, 
weil die Datei "Haus.py" im aktuellen Verzeichnis liegt -- und Dir der 
Modulname "Haus" bekannt ist.

Bevor ich Dir allerdings die Lösung für Dein Problem zeige, möchte ich 
gerne kurz darauf hinweisen, daß es vermutlich einfacher wäre, wenn Du 
Deine einzelnen Häuser erstens sprechender benennen würdest und 
obendrein anstelle von Python-Modulen für Deine Häuser eine 
Konfigurationsdatei benutzen würdest. Python bringt dazu bereits fertige 
Module mit, zum Beispiel json für JSON-Dateien, oder configparser für 
ini-Dateiformate. In solchen Konfigurationsdateien kannst Du allerdings 
keinen Code hinterlegen und ausführen (mit ein paar häßlichen Tricks 
geht's schon, aber...). Allerdings sagst Du ja, daß Du die Dateien nur 
kopierst, insofern nehme ich mal an, daß die meisten Deiner Module 
denselben Code enthalten und die Unterschiede sich vermutlich relativ 
einfach über Konfigurationsdateien darstellen lassen.

Es ist allerdings auch möglich, Module zu laden, deren Namen lediglich 
als String vorliegen. Eine Plugin-basierte Software von mir macht das im 
Kern etwa so:
1
    modules_dir = os.path.join(
2
        os.path.dirname(os.path.abspath(__file__)), MODULES_DIR)
3
    sys.path.append(modules_dir)
4
    for modulefile in os.listdir(modules_dir):
5
        modulename, _ = os.path.splitext(modulefile)
6
        if modulename != '__pycache__':
7
            try:
8
                mod = importlib.import_module(modulename)
9
                mod.register(subparser, modulename)
10
            except:
11
                logging.exception(
12
                    'while loading module "%s":\n'%(modulename),
13
                    exc_info=sys.exc_info()
14
                )
15
                sys.exit(-4)

Dabei besitzt jedes Modul in meinem "modules_dir" jeweils eine Funktion 
"register()" und eine Funktion "main()", die alle dieselben Parameter 
entgegennehmen. Dabei sorgt "register()" dafür, daß die 
Kommandozeilenargumente des Plugin im ArgumentParser des Hauptprogramms 
angemeldet werden, während "main()" die eigentliche Arbeit macht.

Die Module können dabei Dateien "<modulname>.py" oder Verzeichnisse 
"<modulname>" mit einer Datei "__init__.py" darin sein, wichtig ist 
dabei nur, daß die Datei beziehungsweise eine "__init__.py"-Datei die 
betreffenden Funktionen mit ihrer korrekten Signatur zur Verfügung 
stellen.

Wichtig natürlich auch: das Verzeichnis mit den Modulen muß zum Suchpfad 
des Interpreters hinzugefügt werden, hier mit "sys.path.append()".

von R. F. (inet_surfer88)


Lesenswert?

Danke erst mal für die Antworten. Ich habe da jetzt 5 Häuser drin, in 
Kürze kommt ein weiteres dazu. Deswegen wollte ich es jetzt "schnell" 
vereinfachen. Allerdings gefällt mir die Idee mit den angesprochenen 
Config-Dateien recht gut. Und wenn ich mir es richtig überlege, würde es 
das Programm noch an anderer Stelle vereinfachen. Da im Endausbau so ca. 
50 bis 60 Häuser drin sein werden wird sich der Aufwand eines 
grundlegenden Umbaus der Software noch rentieren. Deswegen stelle ich 
das jetzt erst mal ein wenig zurück und nehme das eine Gebäude noch nach 
der alten Softwarestruktur mit auf. Danach werde ich bei Gelegenheit 
mich in die Thematik mit den Config-Dateien einarbeiten und danach in 
Ruhe die Software umstellen.
Ich werde mich dann zur gegebenen Zeit hier wieder melden.

Falls jemand grad zufällig eine Seite im Internet im Kopf hat wo das mit 
den Conig-Dateien gut erklärt ist dann immer her damit. Ansonsten frag 
ich mal die Suchmaschine meines Vertrauens.

von Jemand (Gast)


Lesenswert?

R. F. schrieb:
> Allerdings gefällt mir die Idee mit den angesprochenen
> Config-Dateien recht gut.

Naja, "Idee"...

> Falls jemand grad zufällig eine Seite im Internet im Kopf hat wo das mit
> den Conig-Dateien gut erklärt ist dann immer her damit. Ansonsten frag
> ich mal die Suchmaschine meines Vertrauens.

XML will man eigentlich nicht, weil häßlich und unübersichtlich, und 
YAML hat den Nachteil, daß es in der Standarddistribution keinen Parser 
dafür gibt und man dafür also ein zusätzliches Modul installieren muß; 
bleiben ConfigParser [1] und JSON [2]. Ansonsten hat [3] noch ein paar 
Beispiele.

[1] https://docs.python.org/3/library/configparser.html
[2] https://docs.python.org/3/library/json.html
[3] https://martin-thoma.com/configuration-files-in-python/

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.