Liste erweitern mit variabler Anzahl

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.
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

Hey Leute,

ich stehe vor folgendem Problem: Ich öffne eine Datei, die immer mit 111111 gefüllt ist, und ermittel die Länge des Strings bzw. in dem Fall die Anzahl der 1er. In diesem Beispiel 6. Nun möchte ich diese Anzahl an eine Liste übergeben, sprich eine leere Liste um 6 Mal eine 1 erweitern.

Mein Ansatz bisher:

Code: Alles auswählen

file = open('logfile','r')
laenge = len(file.read())
file.close()
liste = list()
... und jetzt 6 mal bzw. variabel gesehen: laenge * liste.append(1)

Mit was für einer Schleife kann/sollte ich hier arbeiten?
Vielen Dank für Feedback!
Schorlem
User
Beiträge: 40
Registriert: Dienstag 3. Juni 2014, 16:37

Ich finde deinen Ansatz etwas umständlich, einfacher wäre das hier gewesen (ungetestet):

Code: Alles auswählen

file = open('logfile','r')
chars = file.read()
file.close()
liste = []
liste.extend(list(chars))
Diese Nachricht wurde maschinell erstellt und ist daher ohne Unterschrift gültig.
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

Funktioniert und ist tatsächlich deutlich einfacher, vielen Dank das bringt mich weiter! :)

//edit: Aber wenn wir schon bei dem Thema Dateien sind, wie kann ich eine Datei öffnen und ans Ende eine "1" anhängen?
Zuletzt geändert von mobby am Donnerstag 19. Juni 2014, 22:25, insgesamt 1-mal geändert.
BlackJack

@mobby: Da die Liste leer ist, es also gar nicht um das *erweitern* sondern um das *erstellen* geht, würde ich gar keine Schleife verwenden, sondern die Multiplikation auf einer Liste mit einer 1 drin. Ausserdem sollte man wenn möglich die Datei zusammen mit der ``with``-Anweisung öffnen, und dieses Konstrukt das sichere schliessen der Datei übernehmen lassen.

Edit:

Code: Alles auswählen

def main():
    with open('logfile') as one_file:
        result = [int(c) for c in one_file.read()]
Wobei ich mich gerade frage wozu das gut sein soll.

@Schorlem: Das weicht vom beschriebenen Ergebnis ein wenig ab weil dort Zahlen in der Liste stehen und keine Zeichenketten.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

mobby hat geschrieben:Mit was für einer Schleife kann/sollte ich hier arbeiten?
Wenn die Anzahl der Durchläufe bekannt ist, dann verwendet man eine for-Schleife.

In diesem Fall geht es aber auch einfacher:

Code: Alles auswählen

In [17]: [42]*6
Out[17]: [42, 42, 42, 42, 42, 42]
Du solltest dir auch angewöhnen Dateien mittels with-Statement zu öffnen, dann werden sie auch in jedem Fall, so weit das eben möglich ist, geschlossen. "file" ist auch ein schlechter Name für eine Datei. Zum einen sagt er nichts aus, zum anderen verdeckt er den vorhandenen file-Typ.
Das Leben ist wie ein Tennisball.
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

@BlackJack: Das mit den Zahlen ist nicht so wichtig, es geht rein um die Anzahl, egal ob es Zahlen oder Strings sind. Den Hinweis mit der With Anweisung nehme ich gerne an, so funktioniert mein Code jetzt:

Code: Alles auswählen

with open('file','r') as f:
    chars = f.read()

liste = []
liste.extend(list(chars))
print len(liste)
funktioniert!
BlackJack

@mobby: Das ist immer noch deutlich zu umständlich das Du mit einer leeren Liste anfängst statt gleich die Liste zu erstellen. Überleg mal was ``list(chars)`` macht.

Bleibt immer noch die Frage wozu das gut sein soll.
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

@BlackJack: Das steht in Zusammenhang mit einem anderen Thread von mir, klick mich!. Dabei ist mein Ziel an der Stelle an der ich die Liste leading_edges um 1 erweitere noch eine Funtkion abzurufen, die eben eine 1 in eine logdatei schreibt. Sobald die Liste beim Übergeben an die MySQL geleert wird, soll auch der Inhalt der logdatei gelöscht werden und wieder von vorne gezählt werden. Im nächsten schritt wird dann ganz an den Anfang des Skriptes eine Abfrage der Logdatei gestellt, denn startet man das Skript und es sind bereits Einträge in der Logdatei, soll die Liste mit dieser Anzahl Starten bzw. erweitert werden.

Das skript wird nämlich von einem Watchdog überwacht, der über cronjobs alle Minute überprüft ob das Skript noch aktiv ist. Als Schutzmaßnahme, falls das Skript aus irgendeinem Grund heraus abstürtzen sollte.

Okay viel Gelaber, aber vielleicht bringt das etwas Licht ins Dunkle meiner Ansätze. ;)
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Oder so:

Code: Alles auswählen

import os

liste = [1] * os.path.getsize('logfile')
Ungetestet.
In specifications, Murphy's Law supersedes Ohm's.
BlackJack

@mobby: So richtig verstanden habe ich den Sinn der Datei nicht. Das was Dein watchdog macht lässt man üblicherweise entsprechende Programme erledigen die darauf spezialisiert sind. Zum Beispiel die daemontools oder supervisord. Die prüfen auch nicht periodisch, sondern die merken *sofort* wenn der Kindprozess weg stirbt.
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

@BlackJack: Daemontools sieht echt gut aus, damit werde ich mich mal beschäftigen. Sobald mein Skript fertig ist werde ich es auf jeden Fall nochmals hochladen, um es hier auf Herz und Nieren prüfen zu lassen ;) Danke schonmal!
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

Hey Leute, das ist mein ganzes Skript, um mal die Zusammenhänge erkennen zu lassen. Falls etwas auffällt, das man anders bzw. besser gestalten könnte, immer her damit. Zur Info, das Skript überwacht einen digitalen Eingang eines Mikrocontrollers und schreibt, falls HIGH-Werte anfallen, diese gesammelt über ein entsprechendes Zeitintervall in eine Datenbank.

Das Skript sollte dann, momentan noch ein Watchdog, über deamontools überwacht und am Leben gehalten werden.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import time
import os
from contextlib import closing
from itertools import groupby
from threading import Thread
from time import sleep
import MySQLdb as db
import pifacedigitalio as piface


def insert_into_database(timestamp, date, time,  amount):
    with closing(
        db.connect(
                host='localhost',
                user='',
                passwd='',
                db='')
    ) as connection:
        connection.cursor().execute(
            'INSERT INTO Tabelle(Zeitstempel, Datum, Zeit, Impulsanzahl) VALUES (%s, %s, %s, %s)',
            (timestamp, date, time, amount)
        )
        connection.commit()


def blink_led():
    piface.digital_write(0,1)
    sleep(0.1)
    piface.digital_write(0,0)


def write_leading_edge_count(leading_edges):
    intervall = 300
    timestamp = int(time.time() / intervall) * intervall
    while True:
        timestamp += intervall
        time.sleep(max(0, timestamp-time.time()))
        leading_edge_count = len(leading_edges)
        del leading_edges[:]
        reset_logfile()
        datum = str(time.strftime("%d-%m-%Y"))
        uhrzeit = str(time.strftime("%H:%M:%S"))
        insert_into_database(timestamp, datum, uhrzeit, leading_edge_count)


def start_writer(leading_edges):
    thread = Thread(target=write_leading_edge_count, args=(leading_edges,))
    thread.setDaemon(True)
    thread.start()


def write_into_logfile(value):
    with open('logfile','r') as lf:
        lines = lf.readlines()
    lines.insert(0,str(value))
    with open('logfile' + '.new','w') as lfn:
        lfn.writelines(lines)
    os.rename('logfile' + '.new', 'logfile')


def read_logfile(liste):
    with open('logfile','r') as lf:
        chars = lf.read()
    liste.extend(list(chars))


def reset_logfile():
    with open('logfile','r+') as lf:
        lf.truncate()


def iter_values(frequency):
    while True:
        yield piface.digital_read(0)
        sleep(1.0 / frequency)


def iter_changes(frequency):
    for value, _ in groupby(iter_values(frequency)):
        yield value


def main():
    piface.init()
    leading_edges = list()
    read_logfile(leading_edges)
    start_writer(leading_edges)
    for value in iter_changes(100):
        if value == 1:
            leading_edges.append(1)
            write_into_logfile(1)
            blink_led()


if __name__ == '__main__':
    main()
Gruß
mobby
Sirius3
User
Beiträge: 17745
Registriert: Sonntag 21. Oktober 2012, 17:20

@mobby: in Deiner Log-Datei steht also nichts sinnvolles, außer die Anzahl der Bytes. Statt dessen könnte man auch einfach eine Zahl schreiben. Warum schreibst Du einen Zeitstempel, Datum und Zeit in Deine Datenbank? Ein DATETIME-Feld würde doch völlig genügen.
Ist Dir bewusst, dass Deine Abtastfrequenz nicht sonderlich stabil ist und Du nach jedem 1-0-Übergang über 1/10 Sekunde blind bist?
Was für ein Signal willst Du denn überhaupt aufnehmen und was glaubst Du durch die log-Datei zu erreichen?
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

@Sirius3:

1) Logdatei: Du hast recht, momentan stehen nur die Anzahl an Bytes darin, weiß nicht ob es sinnvoller ist eine Zahl zu schreiben? Für meine Zwecke reicht es aber eigentlich so.
2) Zeitstempel: Ja den brauche ich später für das Auslesen der Datenbank, etwas kompliziert, da die über Excel ausgewertet wird ... also brauche ich eben.
3) Abfragefrequenz: Ist mir nicht bewusst, liegt das momentan an der Frequenz an sich? Also ist das über anheben der Frequenz, sprich auf vlt. 200 zu beheben? Um etwas Hintergrundwissen reinzubringen, an dem digitalen Eingang ist eine S0-Schnittstelle angeschlossen. Die kann Impulse mit einer Dauer von 30 ms oder mehr liefern, also sollte die Abtastung so fein sein, dass es auch einen Impuls erkennt, der nur 30 ms dauert. Momentan verstehe ich das so, dass wenn ich eine Frequenz von 100 Eingebe, eine Abfrage pro 0,01 also pro Millisekunde stattfindet, also eine 0 oder eine 1 zurückgegeben wird. Das sollte doch auf jeden Fall ausreichen. Zudem probier ich das ständig in der Praxis aus und bisher gab es keine Bugs in der Hinsicht. Erst wenn ich die Frequenz unter 50 lege kommt es zu Aussetzern.

4) Logdatei2: Die Logdatei dient eigntlich nur dazu, dass falls das Skript aus irgendeinem Grund heraus abstürtzen sollte, die Impulse lokal zwischengespeichert werden.
Sirius3
User
Beiträge: 17745
Registriert: Sonntag 21. Oktober 2012, 17:20

@mobby: Du meinst wohl, Du fragst die Schnittstelle alle 10ms ab, damit hast Du eine dreimal höhere Abtastrate als das Signal. Die Totzeit ist dann drei mal länger als das Signal. Die entscheidende Frage ist ja dann, wie lange die Zeit zwischen den Impulsen ist. Auf jeden Fall mußt Du das Ermitteln der Signale vom Schreiben in die Datei oder Datenbank trennen, also in verschiedenen Threads und über Queues getrennt, nicht Listen, die sind dafür nicht geeignet.

Zum Abstürzen: Programme stürzen nicht einfach ab. Dazu muß es zu einer Fehlfunktion kommen. Also solltest Du Dir erst eine Liste mit allen Fehlern, die passieren können machen und dann die Wahrscheinlichkeit, daß der Fehler auftritt, abschätzen. Auf die meisten Fehler kann Dein Programm, wenn es entsprechend programmiert ist, reagieren. Bleiben noch SIGKILL und Strom weg. Ersterer tritt nicht auf, wenn Du es nicht willst. Zweiterer bedeutet einen Ausfall von mindestens 10 Sekunden, was sich in x verpaßte Signale übersetzen läßt und dann stellt sich die Frage, ob Du mit der Zahl dann überhaupt noch etwas anfangen kannst.
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

Sirius3 hat geschrieben:Auf jeden Fall mußt Du das Ermitteln der Signale vom Schreiben in die Datei oder Datenbank trennen, also in verschiedenen Threads und über Queues getrennt, nicht Listen, die sind dafür nicht geeignet.
Was bietet sich dann an? Eine Variable in der die absolute Anzahl übergeben wird? Und was spricht technisch gegen die Liste?

Und mit der Aussage das Skript stürtzt nicht ab ... dann kann ich mir ja den watchdog/deamontools sparen? Weil sonst hat das ja kaum Fehleranfälligkeit, außer dass jetzt z.B. die Datenbank Verbindung nicht funzt, aber die läuft auf dem gleichen System und es gibt auch keinen ersichtlichen Grund, dass die ausfallen sollte.
BlackJack

@mobby: Gegen eine Liste spricht, dass die nicht threadsafe ist, beziehungsweise man sehr genau aufpassen muss was man damit anstellt und das keine der Operationen Probleme bei nebenläufiger Benutzung machen. Während der Queue-Typ threadsafe ist, was auch entsprechend in der Dokumentation garantiert wird. Ansonsten kann man auch eine Zahl hernehmen und die am besten in einer Klasse kapseln und die kritischen Abschnitte in den Operationen mit Sperren (z.B. `threading.Lock`) absichern. Die Länge einer Liste zum Zählen von etwas zu verwenden, ist IMHO ein Hack/Missbrauch solange man den Inhalt nicht tatsächlich für irgendwas benötigt.

Was das abstürzen angeht: Die Datenbankverbindung kann Probleme machen, auch wenn beides auf dem selben Rechner läuft, zum Beispiel Zeitüberschreitungen wenn das System an sich überlastet ist, Abbrüche der DB-Verbindung weil der Massenspeicher voll ist und die DB deshalb keine Daten mehr entgegen nehmen kann, andere Probleme mit dem Medium oder dem Dateisystem, blockierte Tabellen weil ein anderer Prozess ein Lock auf die Tabelle oder Datensätze hält, (zu) voller Arbeitsspeicher, und alles woran man jetzt gerade nicht denkt, was aber mal passieren kann.
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

@BlackJack: Meinst du mit dem Queue-Typ folgendes?

Code: Alles auswählen

from collections import deque
queue = deque[]
queue.append("1")
BlackJack hat geschrieben:Ansonsten kann man auch eine Zahl hernehmen und die am besten in einer Klasse kapseln und die kritischen Abschnitte in den Operationen mit Sperren (z.B. `threading.Lock`) absichern. Die Länge einer Liste zum Zählen von etwas zu verwenden, ist IMHO ein Hack/Missbrauch solange man den Inhalt nicht tatsächlich für irgendwas benötigt.
Und wie mach ich das? :shock:
BlackJack

@mobby: Eine `collections.deque` kann man auch nehmen, aber üblicherweise verwendet man für die Kommunikation zwischen Threads eine `Queue.Queue`.

Wie man geteilte Daten absichert hängt davon ab wie darauf zugegriffen wird und was dabei jeweils schief laufen kann wogegen man sich deshalb absichern muss.
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

@BlackJack: Wie muss ich mir das vorstellen mit "queue.Queue"? Kann ich alle momentan mit Liste umgesetzen Dinge folgendermaßen ersetzen?

Code: Alles auswählen

import Queue
leading_edges = Queue.Queue()
Und dann anstatt "leading_edges.append(1)" folgendermaßen weiter?

Code: Alles auswählen

leading_edges.put(1)
Und als letztes anstatt "leading_edge_count = len(leading_edges)"

Code: Alles auswählen

leading_edge_count = leading_edges.qsize()
Nur wie leere ich die Reihe wieder? Bzw. muss ich zusätzlich mit "leading_edges.join" und "leading_edges.task_done()" arbeiten? Oder ist so schon alles erfüllt?

Ich weiß, ich stell mich da ein bisschen dappig an, aber ich bin nun mal kein Programmierer und dieser Teil ist momentan nur ein kleiner Bereich meines Projektes. Danke nochmal!
Antworten