List Index out of range

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
IncepTer
User
Beiträge: 2
Registriert: Samstag 28. September 2019, 22:28

Hallo Freunde,

ich bin relativ neu mit Python unterwegs und habe mal eine kleine Frage.

Ich lese aus einer Datei eine Zeile ein und nutze "split" um diesen "String" aufzuteilen.
Die jeweiligen Index speichere ich in weiteren Variablen, um diese dann zu bearbeiten/nutzen.
Wenn ich das Programm allein ausführe, klappt es auch alles ohne Probleme.

Code: Alles auswählen

datensatz1      = file1.read() #"S1:1|W:221.34|P1:1|PZ:0"
daten_liste1    = datensatz1.split("|")

s_status1       = daten_liste1[0]
s_wert1         = daten_liste1[1]
p_an1           = daten_liste1[2]
pump_zaehler1   = daten_liste1[3]
Nun ist mein Ziel, dass diese Python-Datei aus einer anderen aufgerufen wird.
Dazu wird in der 2. Datei ein Wert ermittelt und in der Textdatei gespeichert.
Danach wird die 1. Datei aufgerufen und diese soll dann den Inhalt der s1.txt
verarbeitet werden. Doch hier kommt dann ein Fehler

Code: Alles auswählen

              if "S1" in wert:
                file = open("s1.txt","w")
                file.write(wert)          
            
            cmd = 'sudo python3 DB.py'

            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
            out, err = p.communicate()
            result = out.split('\n')
            for lin in result:
                if not lin.startswith('#'):
                        print(lin)
Fehler:

Code: Alles auswählen

Traceback (most recent call last):
  File "DB.py", line 24, in <module>
    s_wert1             = daten_liste1[1]
IndexError: list index out of range
Hat dazu jemand eine Idee?
Wenn ich die 1. Datei manuell aufrufe, geht alles. Dann nicht mehr.
Ich habe "len" ausgeben lassen und da steht 4.

Danke euch :)
Benutzeravatar
sparrow
User
Beiträge: 4237
Registriert: Freitag 17. April 2009, 10:28

Warum führst du ein anderes Script via subprocess aus?
Wenn tatsächlich die Notwendigkeit besteht, dass der Code dort gekapselt wird (zum Beispiel um ihn aus verschiedenen Progrannen zu nutzen), dann verpack das in eine Funktion in einem Modul und importier das entsprechend.

Und die benötigten Daten bekommt die Funktion als Parameter und nicht aus einer Datei, die das aufrufende Script geschrieben hat.
Sirius3
User
Beiträge: 17822
Registriert: Sonntag 21. Oktober 2012, 17:20

Wass sollen die 1 an den Variablennamen? Wenn man Variablen durchnummeriert, will man eigentlich eine andere Datenstruktur nutzen, z.B. eine Liste.
Was bedeuten die Präfixe s_ oder p_? Keine kryptischen Abkürzungen verwenden.

Ein ›"S1" in wert‹ ist nicht sehr robust. Woher kommt dieser Wert? Wenn er eine bestimmte Struktur hat, warum arbeitest Du hier mit Strings? Beim Schreiben von Text-Dateien sollte man immer ein Encoding mit angeben; Dateien die man öffnet, muß man auch wieder schließen.

Warum rufst Du ein Skript mit root-Rechten auf? Ist das wirklich nötig? Wenn, dann benutze aber kein shell=True, ist hier völlig unnötig. Besser ist es hier, subprocess.run zu benutzen.
Wenn es nur darum geht, einen String an ein Programm zu übergeben, benutze entweder Kommandozeilenargumente, oder Stdin.

Ich habe das Gefühl, Du benutzt die falschen Techniken, kannst genau beschreiben, was Du erreichen willst?
Benutzeravatar
__blackjack__
User
Beiträge: 13236
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@IncepTer: Wenn `len()` 4 als Ergebnis hat, dann kann man auch auf den Index 1 zugreifen. Das kann also nicht stimmen. Du ermittelst dann nicht die Länge der Liste auf die Du dann auch zugreifst.

Anmerkungen zum Quelltext: Es ist in Python unüblich Leerzeichen zu setzen um Gleichheitszeichen auszurichten. Würde ich auch in anderen Programmiersprachen nicht machen, weil das unnötig Arbeit nach sich zieht wenn sich der längste Name in so einem Block ändert. Entweder weil man den Namen selbst ändert oder die Zeile entfernt oder eine Zeile mit einem längeren Namen hinzu kommt. Dann hat man plötzlich ein Diff bei dem sich Zeilen ”ändern” in denen sich eigentlich gar nichts geändert hat.

Man nummeriert keine Namen durch. Wenn man das tut will man sich passende Namen ausdenken oder gar keine einzelnen Namen, sondern eine Datenstruktur verwenden. In diesem Fall scheint es aber einfach nur ganz furchtbar unsinnig überall eine 1 dran zu pappen.

Grunddatentypen haben in Namen nichts verloren. Wenn man im Laufe der Programmentwicklung den Typen ändert, dann hat man entweder falsche, irreführende Namen im Programm, oder man muss überall im Programm die betroffenen Namen ändern. Was bei ausgerichteten Gleichheitszeichen bei Zuweisungen dann noch weitere Änderungen nach sie ziehen kann.

Man sollte Abkürzungen und kryptische Prä- und Suffixe vermeiden. Der Leser sollte nicht rätseln müssen was `p_` und `s_` wohl bedeuten mag.

In Python kann man wirklich sehr oft Indexzugriffe vermeiden. Statt da von Hand die Indexwerte 0 bis 3 hin zu schreiben, kann man auch einfach “unpacking“ auf die Namen bei der Zuweisung machen. Die ganzen sechs Zeilen aus dem ersen Codeschnippsel lassen sich inklusive öffnen und schliessen der Datei in zwei Zeilen ausdrücken:

Code: Alles auswählen

    with open("s1.txt", encoding="ascii") as file:
        s_status, s_wert, p_an, pump_zaehler = file.read().split("|")
Bei Textdateien sollte man immer explizit eine Kodierung angeben. Und man sollte sie immer schliessen, was im zweiten Beispiel nicht passiert — und auch der Grund für das Problem sein kann. Solange die Datei nicht geschlossen wird, muss sie auch noch nicht komplett geschrieben sein. Dann können immer noch Daten im Ausgabepuffer des schreibenden Programms liegen.

Beim zweiten Codeschnippsel ist die Einrückung kaputt — ich vermute mal der Start des externen Programms soll auch noch mit unter das erste ``if`` fallen‽

Es gibt keinen Grund mit ``shell=True`` eine unnötige Shell zwischen den laufenden und den gestarteten Prozess zu setzen. Das ist eine unnötige Fehlerquelle — man weiss beispielsweise nicht ob Ausgaben oder Rückgabecode von der Shell oder von dem in ihr gestarteten Prozess stammen. Das ist ein völlig unnötiges Risiko.

Statt `Popen` würde man hier auch eher `run` verwenden, denn das `Popen`-Objekt wird ja gar nicht wirklich verwendet.

Statt ``split("\n")`` sollte man die `splitlines()`-Methode verwenden, denn da Zeilen mit "\n" abgeschlossen werden, hat man bei ``split("\n")`` am Ende immer eine leere Zeichenkette in der Liste:

Code: Alles auswählen

In [30]: text = """\ 
    ...: one 
    ...: two 
    ...: three 
    ...: """                                                                    

In [31]: text                                                                   
Out[31]: 'one\ntwo\nthree\n'

In [32]: text.split("\n")                                                       
Out[32]: ['one', 'two', 'three', '']

In [33]: text.splitlines()                                                      
Out[33]: ['one', 'two', 'three']
Falls das ``DB.py``-Programm mit dem selben Python ausgeführt werden soll wie das aus dem es aufgerufen wird, dann würde ich da nicht "python3" in die Argumentliste schreiben, sondern ``sys.executable``.

Code: Alles auswählen

            if "S1" in wert:
                with open("s1.txt", "w", encoding="ascii") as file:
                    file.write(wert)
                #
                # TODO If the subprocess should use the same Python than this
                #   program, then use `sys.exetutable` instead of "python3".
                #
                result = subprocess.run(
                    ["sudo", "python3", "DB.py"],
                    stdout=subprocess.PIPE,
                    universal_newlines=True,
                )
                for line in result.stdout.splitlines():
                    if not line.startswith("#"):
                        print(line)
Ist die externe Datei wirklich nötig? Man kann die Daten ja auch ohne Datei dem gestarteten Prozess über sein `stdin` übermitteln (`input`-Argument von `run()`) oder als Argument.

Oder wie sparrow schon schrieb, das ganze gar nicht über einen zusätzlichen Prozess lösen.
Please call it what it is: copyright infringement, not piracy. Piracy takes place in international waters, and involves one or more of theft, murder, rape and kidnapping. Making an unauthorized copy of a piece of software is not piracy, it is an infringement of a government-granted monopoly.
IncepTer
User
Beiträge: 2
Registriert: Samstag 28. September 2019, 22:28

Hallo Ihr und vielen Dank für die sehr ausführlichen Antworten.

Ich rolle es mal etwas anders auf, um vlt. in einigen Punkten Klarheit zu erreichen:

Es läuft eigentlich ein Python Script, welches per angeschlossenen Funk-Modul (RF24) Daten von einem Arduino bekommt.
Diese Daten sollen dann in den lokalen Apache2 verarbeitet werden. Dazu speichere ich die Daten in einer MySQL Datenbank.

Jedoch ist der Aufruf der "DB.py" mit python3 und das Script, welches die Verbindung aufbaut und die Daten vom Arduino empfängt,
wird mit python aufgerufen. Irgendwie bekomme ich den MySQL-Connector nicht mit "python" auf dem Raspberry zum laufen.

Und das ist der Grund, wieso ich einfach mal verschiedene Sachen probiert habe - ist ja alles nur lokal.
Wenn ich die "DB.py" mit "python" starten will, kommt:

Modul not found

Ich werde mich gleich noch einmal durch eure ganzen Hinweise wühlen und befolgen. Danke noch einmal :)
Sirius3
User
Beiträge: 17822
Registriert: Sonntag 21. Oktober 2012, 17:20

Neue Programme sollte man nicht mehr in Python2 schreiben. Also mußt Du sowieso die Kommunikation mit dem Funk-Modul nach Python3 migrieren.
Benutzeravatar
__blackjack__
User
Beiträge: 13236
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@IncepTer: Das "sudo" sollte da dann auch ganz dringend entfernt werden, denn nichts in der Beschreibung deutet darauf hin das man das braucht.
Please call it what it is: copyright infringement, not piracy. Piracy takes place in international waters, and involves one or more of theft, murder, rape and kidnapping. Making an unauthorized copy of a piece of software is not piracy, it is an infringement of a government-granted monopoly.
Antworten