Als Pythonneuling mal eine Frage:
ist es möglich mit Python auch die guten alten "Minidatenbanken" aus den 70ern zu verwenden? Und wenn ja,wie geht das hier? Auch mit "Get" und "Put"? Und ggf. mit den entsprechenden Errorcode-Abfragen um festzustellen, ob die Datenbank gerade auch von jmd anderem benutzt wird?
Ich finde Minidatenbanken, gerade um schnell Werte aus Klassen abzuspeichern, und die u.U auch Multi-User-fähig zur Verfügung zu haben, extrem praktisch.
Info, ggf. auch Codeschnipsel wären nett, wenn Minidatenbanken in Python verwendbar sind
LG SG
Minidatenbanken und Python?
Mir ist jetzt nicht klar, was du unter "Minidatenbanken" verstehst. In der 70ern bzw. den 80ern habe ich noch IMS mit DL/I abgefragt.SG ;-) hat geschrieben:ist es möglich mit Python auch die guten alten "Minidatenbanken" aus den 70ern zu verwenden?
Vielleicht suchst du so etwas wie shelve oder dbm.
Hallo /me
als (noch aktiver) VBAler, der Du bist, hätt ich eig gedacht, dass Du da schon mal was davon gehört hast.
http://www.office-loesung.de/ftopic426439_0_0_asc.php
LG SG
als (noch aktiver) VBAler, der Du bist, hätt ich eig gedacht, dass Du da schon mal was davon gehört hast.
http://www.office-loesung.de/ftopic426439_0_0_asc.php
LG SG
Me.PEBKAC ^^
@SG : ich wüßte jetzt nicht, dass in den 70ern irgendjemand stink normale Dateizugriffe "Datenbank" genannt hätte . Multi-User-Fähigkeit ist wohl auch eher Traum, außer man steckt wirklich viel Arbeit in die "Datenbank"-Umgebung.
Klar kann man binäre Dateien auch mit Python lesen und schreiben. Mit `struct` und `namedtuple` kann man problemlos das, was in Deinem verlinkten Artikel beschrieben wird, realisieren.
Aber warum sollte man? Mit deutlich weniger Aufwand kann man eine SQLite-Datenbank anlegen, und mit Daten füllen.
Klar kann man binäre Dateien auch mit Python lesen und schreiben. Mit `struct` und `namedtuple` kann man problemlos das, was in Deinem verlinkten Artikel beschrieben wird, realisieren.
Aber warum sollte man? Mit deutlich weniger Aufwand kann man eine SQLite-Datenbank anlegen, und mit Daten füllen.
@SG : Dateien auf dem Hintergrundspeicher werden heute eigentlich immer im ”wahlfreien” Zugriff geöffnet, wobei die ”Record-Grösse” ein Byte ist. Das war früher mal etwas besonderes weil wahlfreier Zugriff nicht bei jedem Dateisystem oder Speichermedium (Bandlaufwerke), möglich oder üblich war und es spezielle Dateitypen für verschiedene Aufgaben gab. Der im verlinkten Forenbeitrag erwähnte C64 beziehungsweise die dort verwendeten Dateisysteme auf den Disketten können bei normalen Dateien zum Beispiel nicht beliebige Stellen in der Datei ansteuern ohne vom Anfang der Datei bis zu der Stelle zu lesen. Aber es gab spezielle Dateien mit denen man Datensätze fester Grösse direkt ansteuern kann. Dafür kann man mit diesen Dateien auch wirklich nur das machen und sie nicht wie andere Dateien Byte für Byte sequenziell lesen.
Get und Put musst Du Dir also mit `read()`, `write()`, und `seek()` selber basteln.
Feststellen ob die Datei auch von jemand anderem benutzt wird ist plattformübergreifend nicht so einfach, insbesondere weil der andere das auch absichern muss, es also nicht reicht das er die Datei öffnet, sondern dass er das auch ”ansagt”. Ausserdem gibt es gewisse Konstellationen mit ”Netzlaufwerken” bei denen das Sperren von Dateien nicht zuverlässig funktioniert.
Die Liste der Vorteile, beziehungsweise was sich sonst nur schwer lösen lässt, aus dem verlinkten Forenbeitrag, ist IMHO nicht ganz überzeugend. 1. Daten kann man auch anders speichern. 2. Daten kann man auch bei SQL-Datenbanken einfach als SQL-Dump per Mail weitergeben. Bei SQLite kann man auch die Datei weitergeben. 3. Mich hält ein einfaches flaches Dateiformat mit Datensätzen mit fester Länge nicht davon ab manuell einzugreifen. Und ich denke ich bin nicht der Einzige. 4. Der Aufwand ist verglichen mit fertigen Lösungen relativ hoch. 5. Welche Probleme mit Trennzeichen bei CSV? Darum kümmert sich das `csv`-Modul. 6. Unterschiedliche Datensatzlängen in CSV sind nur ein Problem wenn man wahlfreien Zugriff braucht ohne die Daten in den Speicher zu lesen.
Wie Sirius3 schon schrieb kann man sich so eine Datei mit Datensätzen fester länge mit dem `struct`-Modul und normalen Dateioperationen relativ einfach selber basteln. Bleibt alleine die Frage wozu das gut sein soll, weil man letztendlich dann sein eigenes DBMS schreibt, nur halt schlechter als das was es schon ausgereift und getestet gibt. SQLite3 ist Bestandteil der Python-Standardbibliothek, und auch `shelve`, `csv`, oder `json`, was man alles für kleine ”Datenbanken” verwenden kann. Wobei SQLite3 und `shelve` durchaus auch mit grösseren Datenmengen klar kommen können. Bei SQLite3 hätte man zudem den Vorteil eine SQL-Datenbank zu haben und die Daten deshalb auch auf ein grösseres DBMS umziehen zu können, wenn das nötig wird. Wenn man eine Abstraktionsschicht wie SQLAlchemy verwendet und nichts DB-spezifisches, geht das wahrscheinlich sogar recht problemlos.
Wenn man unbedingt eine Datei mit fester Datensatzgrösse haben möchte, würde ich auch nicht unbedingt selber etwas erfinden sondern etwas nehmen was auch in die 70er (oder davor) zurück geht, nämlich DBF-Dateien. Die können von den meisten Tabellenkalkulationen und Datenbank-GUIs in Office-Paketen verwendet werden. Ich würde so eine Datei aber nicht erstellen wenn ich nicht müsste, also wenn mich nicht eine andere Software dazu zwingt. Zum Beispiel sind DBF-Dateien Bestandteil von ESRI Shapefiles die man oft im GIS-Umfeld findet.
Get und Put musst Du Dir also mit `read()`, `write()`, und `seek()` selber basteln.
Feststellen ob die Datei auch von jemand anderem benutzt wird ist plattformübergreifend nicht so einfach, insbesondere weil der andere das auch absichern muss, es also nicht reicht das er die Datei öffnet, sondern dass er das auch ”ansagt”. Ausserdem gibt es gewisse Konstellationen mit ”Netzlaufwerken” bei denen das Sperren von Dateien nicht zuverlässig funktioniert.
Die Liste der Vorteile, beziehungsweise was sich sonst nur schwer lösen lässt, aus dem verlinkten Forenbeitrag, ist IMHO nicht ganz überzeugend. 1. Daten kann man auch anders speichern. 2. Daten kann man auch bei SQL-Datenbanken einfach als SQL-Dump per Mail weitergeben. Bei SQLite kann man auch die Datei weitergeben. 3. Mich hält ein einfaches flaches Dateiformat mit Datensätzen mit fester Länge nicht davon ab manuell einzugreifen. Und ich denke ich bin nicht der Einzige. 4. Der Aufwand ist verglichen mit fertigen Lösungen relativ hoch. 5. Welche Probleme mit Trennzeichen bei CSV? Darum kümmert sich das `csv`-Modul. 6. Unterschiedliche Datensatzlängen in CSV sind nur ein Problem wenn man wahlfreien Zugriff braucht ohne die Daten in den Speicher zu lesen.
Wie Sirius3 schon schrieb kann man sich so eine Datei mit Datensätzen fester länge mit dem `struct`-Modul und normalen Dateioperationen relativ einfach selber basteln. Bleibt alleine die Frage wozu das gut sein soll, weil man letztendlich dann sein eigenes DBMS schreibt, nur halt schlechter als das was es schon ausgereift und getestet gibt. SQLite3 ist Bestandteil der Python-Standardbibliothek, und auch `shelve`, `csv`, oder `json`, was man alles für kleine ”Datenbanken” verwenden kann. Wobei SQLite3 und `shelve` durchaus auch mit grösseren Datenmengen klar kommen können. Bei SQLite3 hätte man zudem den Vorteil eine SQL-Datenbank zu haben und die Daten deshalb auch auf ein grösseres DBMS umziehen zu können, wenn das nötig wird. Wenn man eine Abstraktionsschicht wie SQLAlchemy verwendet und nichts DB-spezifisches, geht das wahrscheinlich sogar recht problemlos.
Wenn man unbedingt eine Datei mit fester Datensatzgrösse haben möchte, würde ich auch nicht unbedingt selber etwas erfinden sondern etwas nehmen was auch in die 70er (oder davor) zurück geht, nämlich DBF-Dateien. Die können von den meisten Tabellenkalkulationen und Datenbank-GUIs in Office-Paketen verwendet werden. Ich würde so eine Datei aber nicht erstellen wenn ich nicht müsste, also wenn mich nicht eine andere Software dazu zwingt. Zum Beispiel sind DBF-Dateien Bestandteil von ESRI Shapefiles die man oft im GIS-Umfeld findet.
Bevor Du Dir ein eigenes binäres Format als Alternative zu Datenbanken ausdenkst, schau Dir mal hdf5 an.
In hdf5 werden die Daten binär in einer Datei gespeichert. Baumstruktur, Tabellen, Arrays und Bildformate werden unterstützt. Der Zugriff auf die Daten ist extrem performant. Du kannst mit externen Tools in die Daten reinschauen und auch mit anderen Programmiersprachen darauf zugreifen.
Die Python Schnittstelle zu hdf5 heißt PyTables.
In hdf5 werden die Daten binär in einer Datei gespeichert. Baumstruktur, Tabellen, Arrays und Bildformate werden unterstützt. Der Zugriff auf die Daten ist extrem performant. Du kannst mit externen Tools in die Daten reinschauen und auch mit anderen Programmiersprachen darauf zugreifen.
Die Python Schnittstelle zu hdf5 heißt PyTables.
Alles klar,
(fast jedenfalls).^^ Ich werd mir die einzelnen Lösungsvorschläge auf jeden Fall genauer ansehen. SQL wäre möglicherweise wirklich eine Lösung, der Grund, warum ich unter VBA bisher darauf verzichtet hatte, war in erster Linie ein MS-Office-spezifischer: man müßte unter Office für jedes Office, das dann damit arbeiten soll, gesondert DLLS installieren, was erstens einmal schon ein gewisser Arbeitsaufwand für sich ist; - darüberhinaus, müsste man aber auch bei unterschiedlichen Betriebssystemskonfigurationen und unterschiedlichen Officeversionen das VBA-Script für jeden einzelnen Rechner anpassen, da die entsprechenden DLLS (32/64bit; Office 2007/2010 etc.) untereinander nicht kompatibel sind. Minidatenbanken benötigen unter Office keine zusätzlichen DLLs, weswegen das Problem dadurch schlicht und ergreifend entfällt.
Ein weiterer Vorteil von Minidatenbanken gegenüber aufwändigen relationalen Datenbanken ist allerdings ihre enorme Gechwindigkeit: da nicht jedesmal die ganze Datenbank durchsucht werden muss, um einen Datensatz zu finden und zu ändern, sondern man einfach einen Integer-Zeiger verwenden kann, läuft der Speichervorgang beim Ändern von Datensätzen in Micro- wenn nicht sogar Nanosekunden ab. Man kann das Speichern also direkt aus dem Property_Get einer Klassenvariablen feuern, ohne dadurch nennenswerte Verzögerungen im Programmablauf befürchten zu müssen. (Jedenfalls bei den Anwendungen, dich ich bisher geschrieben habe. Da ging es z.B. darum, einen Textstring oder eine Zahl während des Eintippens zu speichern, und diesen Wert damit SOFORT allen beteiligten Clients zur Verfügung zu stellen)
Dank an alle, für Eure zahlreichen Beiträge, jetzt muss ich erst mal ein bissel recherchieren
LG SG
(fast jedenfalls).^^ Ich werd mir die einzelnen Lösungsvorschläge auf jeden Fall genauer ansehen. SQL wäre möglicherweise wirklich eine Lösung, der Grund, warum ich unter VBA bisher darauf verzichtet hatte, war in erster Linie ein MS-Office-spezifischer: man müßte unter Office für jedes Office, das dann damit arbeiten soll, gesondert DLLS installieren, was erstens einmal schon ein gewisser Arbeitsaufwand für sich ist; - darüberhinaus, müsste man aber auch bei unterschiedlichen Betriebssystemskonfigurationen und unterschiedlichen Officeversionen das VBA-Script für jeden einzelnen Rechner anpassen, da die entsprechenden DLLS (32/64bit; Office 2007/2010 etc.) untereinander nicht kompatibel sind. Minidatenbanken benötigen unter Office keine zusätzlichen DLLs, weswegen das Problem dadurch schlicht und ergreifend entfällt.
Ein weiterer Vorteil von Minidatenbanken gegenüber aufwändigen relationalen Datenbanken ist allerdings ihre enorme Gechwindigkeit: da nicht jedesmal die ganze Datenbank durchsucht werden muss, um einen Datensatz zu finden und zu ändern, sondern man einfach einen Integer-Zeiger verwenden kann, läuft der Speichervorgang beim Ändern von Datensätzen in Micro- wenn nicht sogar Nanosekunden ab. Man kann das Speichern also direkt aus dem Property_Get einer Klassenvariablen feuern, ohne dadurch nennenswerte Verzögerungen im Programmablauf befürchten zu müssen. (Jedenfalls bei den Anwendungen, dich ich bisher geschrieben habe. Da ging es z.B. darum, einen Textstring oder eine Zahl während des Eintippens zu speichern, und diesen Wert damit SOFORT allen beteiligten Clients zur Verfügung zu stellen)
Dank an alle, für Eure zahlreichen Beiträge, jetzt muss ich erst mal ein bissel recherchieren
LG SG
Me.PEBKAC ^^
Die Zeiten bekommst du aber nur, wenn die "Minidatenbank" im RAM liegt. Dann kannst du aber auch eine SQLite im RAM verwenden.SG ;-) hat geschrieben:Ein weiterer Vorteil von Minidatenbanken gegenüber aufwändigen relationalen Datenbanken ist allerdings ihre enorme Gechwindigkeit: da nicht jedesmal die ganze Datenbank durchsucht werden muss, um einen Datensatz zu finden und zu ändern, sondern man einfach einen Integer-Zeiger verwenden kann, läuft der Speichervorgang beim Ändern von Datensätzen in Micro- wenn nicht sogar Nanosekunden ab.
@SG : Ich weiss nicht was für Vorstellungen Du von der Speicherung von Daten in relationalen Datenbanken hast, aber wenn man eine ganze Zahl als Primärschlüssel verwendet, also letztendlich den gleichen Typ den man bei einer ”Minidatenbank” als Schlüssel für den Zugriff benutzt, wird kein vernünftiges DBMS dafür alle Datensätze sequentiell durchgehen müssen um den passenden Eintrag in der Datei zu finden. Für einen Primärschlüssel wird üblicherweise implizit ein Index angelegt. Die dadurch entstehende Verzögerung dürfte auf aktueller Hardware so minimal sein das sie von anderen Faktoren, wie den Schreibzugriff selbst, locker überschattet wird.
Im Gegenteil sehe ich bei einer Datenbankdatei mit sequentiell gespeicherten Datensätzen mehr lineare Suchen über die gesamte Datei wenn man mal etwas anhand anderer Kriterien als der Datensatznummer haben möchte. Wo ich selbst bei sequentiellen Suchen in relationalen DBMS dann Vorteile bei mehreren Suchen durch Caches erwarte, oder halt auch die Möglichkeit Indexe anzulegen um das zu beschleunigen. Alles Sachen die man sich bei einer selbst verwalteten, einfachen Datenbankdatei erst selber programmieren müsste.
Das mit dem Multiclientzugriff halte ich übrigens schon micht mehr für trivial selber zu schreiben, im Gegensatz zum lesen und schreiben von Records in eine Datei. Dafür muss man sich eine sinnvolle API ausdenken und die dann auch noch fehlerfrei und robust implementieren. Alleine deswegen würde ich schon ein fertiges DBMS nehmen von dem man weiss, dass es das Problem bereits gelöst hat. Wenn man wirklich nur IDs als Schlüssel benötigt, kann man statt eines relationen DBMS auch etwas anderes nehmen was einen einfachen Key/Value-Store ermöglicht. CouchDB, MemcacheDB, MongoDB, oder Redis kommen mir da so spontan in den Sinn.
Im Gegenteil sehe ich bei einer Datenbankdatei mit sequentiell gespeicherten Datensätzen mehr lineare Suchen über die gesamte Datei wenn man mal etwas anhand anderer Kriterien als der Datensatznummer haben möchte. Wo ich selbst bei sequentiellen Suchen in relationalen DBMS dann Vorteile bei mehreren Suchen durch Caches erwarte, oder halt auch die Möglichkeit Indexe anzulegen um das zu beschleunigen. Alles Sachen die man sich bei einer selbst verwalteten, einfachen Datenbankdatei erst selber programmieren müsste.
Das mit dem Multiclientzugriff halte ich übrigens schon micht mehr für trivial selber zu schreiben, im Gegensatz zum lesen und schreiben von Records in eine Datei. Dafür muss man sich eine sinnvolle API ausdenken und die dann auch noch fehlerfrei und robust implementieren. Alleine deswegen würde ich schon ein fertiges DBMS nehmen von dem man weiss, dass es das Problem bereits gelöst hat. Wenn man wirklich nur IDs als Schlüssel benötigt, kann man statt eines relationen DBMS auch etwas anderes nehmen was einen einfachen Key/Value-Store ermöglicht. CouchDB, MemcacheDB, MongoDB, oder Redis kommen mir da so spontan in den Sinn.
Mal nebenbei so was in der Richtung wie in dem verlinkten Forenbeitrag implementiert:
So sieht die Datei als Hexdump aus (also der Anfang, ganz wäre etwas lang ):
Code: Alles auswählen
#!/usr/bin/env python
# coding: utf-8
from __future__ import print_function
import errno
import os
from collections import namedtuple
from datetime import date as Date, datetime as DateTime
from random import randint, random, shuffle
from struct import Struct
class RecordFile(object):
def __init__(self, file_or_filename, struct):
if isinstance(file_or_filename, basestring):
try:
self.file = open(file_or_filename, 'r+')
except IOError as error:
if error.errno == errno.ENOENT:
self.file = open(file_or_filename, 'w+')
else:
raise
else:
self.file = file_or_filename
self.struct = struct
def __enter__(self):
return self
def __exit__(self, *_args):
self.close()
def __len__(self):
current_position = self.file.tell()
self.file.seek(0, os.SEEK_END)
result = self.tell_record()
self.file.seek(current_position)
return result
def __getitem__(self, index):
if index < 0:
index += len(self)
if not 0 <= index < len(self):
raise IndexError
return self.read_record(index)
def __setitem__(self, index, data):
self.write_record(data, index)
def tell_record(self):
return self.file.tell() // self.struct.size
def seek_record(self, number):
self.file.seek(self.struct.size * number)
def read_record(self, number=None):
if number is not None:
self.seek_record(number)
return self.struct.unpack(self.file.read(self.struct.size))
def write_record(self, data, number=None):
if number is not None:
self.seek_record(number)
self.file.write(self.struct.pack(*data))
def close(self):
self.file.close()
Record = namedtuple('Record', 'id name date value')
class RecordStruct(object):
DATE_FORMAT = '%Y%m%d'
def __init__(self, type_=tuple):
self.struct = Struct('<I 20s 8s d')
self.size = self.struct.size
self.type_ = type_
def pack(self, id_, name, date, value):
return self.struct.pack(
id_, name, format(date, self.DATE_FORMAT), value
)
def unpack(self, data):
id_, name, date, value = self.struct.unpack(data)
return self.type_(
id_,
name.rstrip('\0'),
DateTime.strptime(date, self.DATE_FORMAT),
value
)
def create_records(count=100):
ids = range(count)
shuffle(ids)
return [
Record(
id_,
'Test {0}'.format(id_),
Date(randint(1900, 2014), randint(1, 12), randint(1, 28)),
random() * 1000
)
for id_ in ids
]
def struct_test(filename):
records = create_records()
with RecordFile(filename, RecordStruct(Record)) as record_file:
print(len(record_file))
for record in records:
record_file.write_record(record)
print(len(record_file))
print(record_file[0])
for record in record_file:
print(
'#{0.id}\n'
' {0.name:>20} = {0.value:-7.3f} @ {0.date:%Y-%m-%d}\n'
'----'.format(record)
)
record_file[42] = Record(4711, 'The Answer', Date.today(), 42.23)
print(record_file[-1])
print(record_file[42])
def main():
filename = 'test.dat'
try:
os.remove(filename)
except OSError:
pass
struct_test(filename)
if __name__ == '__main__':
main()
Code: Alles auswählen
bj@god:~$ ls -l test.dat
-rw-rw-r-- 1 bj bj 4000 Sep 1 20:55 test.dat
bj@god:~$ hd test.dat | head
00000000 11 00 00 00 54 65 73 74 20 31 37 00 00 00 00 00 |....Test 17.....|
00000010 00 00 00 00 00 00 00 00 31 39 36 39 30 31 31 32 |........19690112|
00000020 72 0e a6 32 f4 b5 7e 40 06 00 00 00 54 65 73 74 |r..2..~@....Test|
00000030 20 36 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 6..............|
00000040 31 39 34 33 30 32 30 39 c1 cd fd 68 51 35 8d 40 |19430209...hQ5.@|
00000050 46 00 00 00 54 65 73 74 20 37 30 00 00 00 00 00 |F...Test 70.....|
00000060 00 00 00 00 00 00 00 00 31 39 34 30 31 32 31 39 |........19401219|
00000070 58 cb ab d8 8a 88 86 40 3e 00 00 00 54 65 73 74 |X......@>...Test|
00000080 20 36 32 00 00 00 00 00 00 00 00 00 00 00 00 00 | 62.............|
00000090 31 39 35 32 30 31 32 38 d9 68 20 fe e3 e8 8c 40 |19520128.h ....@|