Seite 1 von 1
Klasse als Datenspeicher, die nur einmal existiert
Verfasst: Donnerstag 21. August 2014, 13:40
von Papp Nase
Hallo,
ich möchte Informationen in einer Klasse speichern, da ich keine globale Variable benutzen möchte. Der Inhalt soll einfach erstmal ein Integer sein und zwei Methoden haben:
put und get.
Code: Alles auswählen
class Datenspeicher:
def __init__(self):
self._value=0 # Initialwert
def get(self):
return(self._value)
def put(self, value):
self._value=value
speicher1 = Datenspeicher()
speicher2 = Datenspeicher()
speicher1 und speicher2 sollen aber nicht zwei unterschiedliche Datenspeicher sein, sondern die gleichen. Ich habe im Internet gelesen, dass ich dann die Klasse so zurechtmachen muss, dass es sie nur einmal gibt - Singleton. Dazu habe ich unterschiedliche Beispiele gefunden, die ich aber noch nicht verstanden habe. Hier auf dem Link habe ich einige Beispiele gefunden:
http://code.activestate.com/recipes/52558/
Zunächst einmal kommt es vor, dass in der Klasse Variablen stehen, die nicht in einer Funktion stehen, z.B. so:
Code: Alles auswählen
class Example():
__instance=None
def __init__(self):
...
def __call__(self):
...
Da __instance nicht in einer Funktion steht, ist __instance in diesem Fall eine gloabe Variable innerhalb der Klasse Example?
Es gibt einige Beispiele in dem Link mit einer Funktion __call__(self): ... und nicht immer ist eine Funktion __init__(self): ... vorhanden. Muss man nicht zwangsläufig eine Methode __init__(self): ... in einer Klasse haben, die irgendwas macht beim Aufrufen? Alles, was mit zwei Unterstrichen anfängt wie __init... oder nun auch das __call__.. hat ja irgendwie eine besondere Bedeutung. __init__ wird aufgerufen, wenn das Objekt erzeugt wird mit var = Example(), dann wird. Das habe ich nun verstanden. Wann wird denn die Funktion __call__ ausgeführt?
In einigen der Beispiele gibt es den Fall, dass in der Klasse eine weitere Klasse erzeugt wird:
Ist __impl auch eine spezielle Funktion wie z.B. __init__ oder ist es ein selbstgewählter Name?
Wenn man eine Singleton-Klasse erzeugt und man zwei Threads hat, die Daten von dort haben wollen oder hineinschaufeln, muss man dann ggf. auch darauf achten, dass man den Zugriff für Prozess 2 solange sperrt, bis Prozess 1 fertig ist - oder macht das der Interpreter von selber mit der Zugriffssteuerung, weil man von selber dafür sorgt, dass man ein Singleton-Pattern gewählt hat?
Re: Klasse als Datenspeicher, die nur einmal existiert
Verfasst: Donnerstag 21. August 2014, 13:59
von BlackJack
Wenn Du das nicht als globale Variable haben möchtest, dann kannst Du das auch nicht in ein Singleton stecken, denn das ist doch auch eine globale Variable! Ansonsten kann man technisch Magie betreiben um ein Singleton zu erstellen, das würde ich aber nicht machen. Entweder nimmt man ein Modul, denn das sind in Python Singletons, oder eine normale Klasse von der man halt einfach nur ein Exemplar erstellt.
Re: Klasse als Datenspeicher, die nur einmal existiert
Verfasst: Donnerstag 21. August 2014, 14:07
von Papp Nase
BlackJack hat geschrieben:... Entweder nimmt man ein Modul, denn das sind in Python Singletons, oder eine normale Klasse von der man halt einfach nur ein Exemplar erstellt.
Was meinst Du denn mit Modul? Unter Modul verstehe ich jetzt, dass man eine Datei erstellt, z.B. modul1.py und diese Datei mit import in einem anderen Programm einbindet. Ich hab auch nochmal nach Modul python gegooglet und da kam das heraus.
Wenn ich z.B. jetzt in einer Datei schreibe:
modul.py:
datenspeicher=1.0
und in einer anderen Datei:
main.py:
import modul
modul.datenspeicher=10
meinst Du das so?
Re: Klasse als Datenspeicher, die nur einmal existiert
Verfasst: Donnerstag 21. August 2014, 14:11
von BlackJack
@Papp Nase: Ja, so war das gemeint. Das Modulobjekt ist immer das selbe, egal in welchem anderen Modul man das importiert.
Re: Klasse als Datenspeicher, die nur einmal existiert
Verfasst: Donnerstag 21. August 2014, 14:20
von snafu
Was soll das ganze Vorgehen überhaupt für einen Sinn haben? Wenn man eine Klasse benutzt, die allein dazu dient, einen Wert mit Getter- und Setter-Methoden zu umhüllen und dann ein Exemplar dieser Klasse global ablegt, dann hat man ja letztlich auch wieder eine globale Variable erzeugt - nur über Umwege. Was hat einem das dann gebracht?
Re: Klasse als Datenspeicher, die nur einmal existiert
Verfasst: Donnerstag 21. August 2014, 14:41
von jens
Genau, welches Problem soll denn überhaupt gelöst werden?
Geht es um "Programm Einstellungen" ?
Im einfachsten Fall, gehe ich dann hin und mache ein paar "Konstanten":
Code: Alles auswählen
EINSTELLUNG_A = 12
EINSTELLUNG_B = "foo"
def mein_programm():
for x in xrange(EINSTELLUNG_A):
...
Oder aber eine "Config-Klasse":
Code: Alles auswählen
class Config(object):
EINSTELLUNG_A = 12
EINSTELLUNG_B = "foo"
def mein_programm(cfg):
for x in xrange(cfg.EINSTELLUNG_A):
...
if __name__ == "__main__":
cfg=Config()
mein_programm(cfg)
Gibt noch zig Variationen die man so machen könnte... z.B. das Config() eine .ini Datei mit Einstellungen liest. Dann auf modulebene eine Instanz davon machen und die überall benutzten oder halt "rumreichen"...
Re: Klasse als Datenspeicher, die nur einmal existiert
Verfasst: Donnerstag 21. August 2014, 14:51
von Papp Nase
snafu hat geschrieben:Was hat einem das dann gebracht?
Ich habe mal in einer anderen Programmiersprache programmiert. Dort konnte man zum Hin- und Herschaufeln von Daten eine globale Variable erstellen und dann bei den Variableneigenschaften sagen, dass die Variable vom Typ Singleton sein soll. Und wenn ich das angeklickt habe, dann gab es keine Laufzeitprobleme beim Zugriff auf die Variable. Wenn zwei Prozesse gleichzeitig auf die identische Ressource zugreifen wollten, dann hat der Compiler selber dafür gesorgt, dass der Prozess, der zuerst da war, drangekommen ist und der zweite Prozess solange gewartet hat, bis die Operation bei Prozess abgeschlossen war.
Bis jetzt war es in Python so - wenn ich nur eine Variable (egal ob Array, Liste oder auch nur ein Einzelwert), dann musste ich ihn an der Wurzel definieren z.B. in der Main-Methode und mittels Argumente an alle Unterfunktionen durchreichen. Und so kam mir die Idee, das mit dem Singleton in Python auch hinzukriegen.
Re: Klasse als Datenspeicher, die nur einmal existiert
Verfasst: Donnerstag 21. August 2014, 15:08
von jens
Und zwischen was willst du Variablen austauschen? Prozesse erzeugt mit dem multiprocess Modul oder Threads?
Re: Klasse als Datenspeicher, die nur einmal existiert
Verfasst: Donnerstag 21. August 2014, 15:31
von BlackJack
@Papp Nase: Du willst also gar kein Singleton sondern ein Objekt bei dem der Zugriff threadsicher ist. Das ist im allgemeinen *keine* Eigenschaft von Singletons. Das dazu passende Fachwort ist da eher „Monitor”. *So* etwas könnte man sich in Python natürlich auch basteln, allerdings muss man da aufpassen, dass das natürlich nur den direkten Zugriff auf das Objekt schützt. Wenn sich dann zwei Threads über dieses Objekt die selbe Liste, oder irgend ein anderes veränderbares Objekt abholen, können die nach dem Zugriff natürlich damit unsichere Sachen anstellen.
Statt Prozesse meinst Du sicher Threads, oder? Bei Prozessen ist ja eigentlich gegeben das normalerweise gar keinen gemeinsamen Speicher haben.
Re: Klasse als Datenspeicher, die nur einmal existiert
Verfasst: Donnerstag 21. August 2014, 16:10
von jens
Ob nun Threads oder Prozesse: Generell verlangsamt sowas den Ablaufe, weil halt irgendwie synchronisiert werden muß.
Keine Ahnung ob das auch zutrifft, wenn 99% gelesen wird und nur 1% geändert.
Re: Klasse als Datenspeicher, die nur einmal existiert
Verfasst: Donnerstag 21. August 2014, 23:10
von Papp Nase
Ich habe mir hier ein Beispiel ausgedacht, was mein Problem verdeutlichen soll. Im Netz hab ich ein Beispiel für einen Ringspeicher gefunden. Der Ringspeicher hat zwei Funktionen: .add(value) zum anfügen und .array(), um den Inhalt auszugeben. Mit .add will ich - wenn es später mal klappt - in einem Thread Daten einfügen in den Ringspeicher. Die Inhalte des Ringspeichers sollen in einem Graphen angezeigt werden und nach einer festgesetzten Zeit auch aktualisiert werden. Ich habe Angst, dass es bei dem Reinschreibeprozess bei der add-Methode und dem Lesen mit der array-Methode zu Problemen kommen könnte, da sie ja in verschiedenen Threads ablaufen.
Ich habe bei diesem Beispiel allerdings noch ein Problem - ich weis nicht, wie ich einen Plot aktualisieren soll. Ich habe - um den Plot in ein tkinter-Frame einzubauen, mit Funktionen aus matplotlib.figure gearbeitet, nicht mit matplotlib.pyplot. Pyplot hat nicht funktioniert. Bei Pyplot gibt es die Methoden pyplot.ion(), um den interaktiven Modus einzuschalten, dann erreichte ich es in einem anderen Beispiel, dass bei Veränderung des Datenarrays auch der Plot aktualisiert wurde. Jetzt möchte ich die Daten erstmal per Tastendruck aktualisieren mit der Methode refresh_plot. Wenn das klappt, dann kann ich später - wie in diesem Beispiel:
http://www.python-forum.de/viewtopic.ph ... 05&start=0 mit der after-Methode die Refresh-Funktion erneut aufrufen.
In einem anderen Beispiel habe ich zur Aktualisierung auch etwas mit funcanimation gefunden:
http://jakevdp.github.io/blog/2012/08/1 ... -tutorial/
Aber dieses Beispiel arbeitet auch mit der Pyplot-Klasse und nicht mit der figure-Klasse. Ich habe es aber nicht hinbekommen, mit der Pyplot-Klasse die Einbindung in einen tkinter-Frame hinzukriegen
Code: Alles auswählen
import numpy as np
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.backend_bases import key_press_handler
from matplotlib.figure import Figure
import matplotlib.animation as animation
from matplotlib import pyplot as plt
import random
import time
try:
import tkinter as tk
except:
import Tkinter as tk
class FIFOBuffer(object):
"""
A circular FIFO buffer implemented on top of numpy.
"""
__instance=None
def __init__(self, shape, dtype=np.float32, filled=False):
self._cache = np.zeros(shape, dtype)
self._values = np.zeros(shape, dtype)
self.shape = shape
self._cached=True
if filled:
self._ind = self.shape[0]
else:
self._ind = 0
self._cached = False
def add(self, value):
"""
Add a value to the buffer.
"""
ind = self._ind % self.shape[0]
self._values[ind] = value
self._ind += 1
self._cached = False
def array(self):
"""
Returns a numpy array containing the last stored values.
"""
if self._ind < self.shape[0]:
return self._values[:self._ind]
if not self._cached:
ind = self._ind % self.shape[0]
self._cache[:self.shape[0] - ind] = self._values[ind:]
self._cache[self.shape[0] - ind:] = self._values[:ind]
self._cached = True
return self._cache
def __len__(self):
return min(self._ind, self.shape[0])
def create_data(channels, container):
for i in range (0, channels):
container[i].add(random.random()*10)
def print_data(channels, container):
for i in range (0, channels):
print (container[i].array())
class PlotDisplay(tk.Frame):
def __init__(self, master, channels, data, style_args, refreshtime=100):
# tk.Frame.__self__(self, master=master)
self._master = master
self._data = data
self._styleargs = style_args
self._refreshtime = refreshtime
self._channels = channels
self._fig = Figure()
#self._fig = plt.Figure()
#plt.ion()
self._plot1 = self._fig.add_subplot(111)
self._canvas = FigureCanvasTkAgg(self._fig, master=self._master)
self._canvas.show()
self._canvas._tkcanvas.pack()
print ("in plotdisplayinit")
self._init_plot()
def _init_plot(self):
print ("in initplot")
for i in range (0, self._channels):
print (self._data[i].array())
# plt.plot(self._data[i].array(), self._styleargs[i])
self._plot1.plot(self._data[i].array(), self._styleargs[i])
def refresh_plot(self):
print ("in refresh_plot")
create_data(self._channels, self._data)
print_data (self._channels, self._data)
def main ():
root = tk.Tk()
def do_exit():
root.quit()
root.destroy()
plotframe = tk.Frame(master=root)
buttonframe = tk.Frame(master=root)
exit_button = tk.Button (buttonframe, text=" Beenden ", width=21, command=do_exit)
exit_button.pack()
refresh_button = tk.Button (buttonframe, text = " Refresh display ", width=21)
refresh_button.pack()
buttonframe.grid(row=0, column=2)
plotframe.grid(row=0, column=1)
style_args=['g-x', 'b*-', 'g','y--','r','b']
channels=6
values = 10
maxdata=channels*values
print ("Maxdata ---> %s" %str(maxdata))
# init data_container
data_container=[]
for i in range (0,channels):
data_container.append( FIFOBuffer(shape=[(values)], dtype=np.float16, filled=True) )
create_data(channels, data_container)
create_data(channels, data_container)
create_data(channels, data_container)
create_data(channels, data_container)
display = PlotDisplay (plotframe, channels, data_container, style_args)
refresh_button['command']=display.refresh_plot
"""
fig = plt.Figure()
for i in range (0, channels):
plt.plot(data_container[i].array(), style_args[i])
plt.show()
"""
root.mainloop()
print ("... PRG_end ...")
if __name__ == "__main__":
main ()
Re: Klasse als Datenspeicher, die nur einmal existiert
Verfasst: Donnerstag 21. August 2014, 23:44
von Sirius3
@Papp Nase: Dein Ringspeicher ist kein Singleton, wäre ja auch blöd wenn's einer wäre, und braucht auch keinen besonderen Zugriffsschutz, wenn es nur einen Schreibthread gibt und Du das aktuell zu schreibende Element aus dem Lesezugriff herausnimmst. Also welches Problem soll damit verdeutlicht werden?
Re: Klasse als Datenspeicher, die nur einmal existiert
Verfasst: Freitag 22. August 2014, 00:09
von snafu
Generell lassen sich konkurrierende Zugriffe so schützen:
Code: Alles auswählen
import threading
class ThreadsafeData(object):
def __init__(self, initial_value=None):
self._value = initial_value
self._lock = threading.Lock()
def get(self):
with self._lock:
return self._value
def set(self, value):
with self._lock:
self._value = value
Wenn ein "Lock-Block" noch aktiv ist und gleichzeitig ein weiterer Zugriff auf einen mit diesem Lock geschützten Codeabschnitt erfolgen soll - hier also: erneuter Aufruf von ``.get()`` oder ``set()`` aus einem anderen Thread heraus -, dann blockiert die Ausführung für Thread 2 solange bis Thread 1 den Block verlassen hat, denn erst dann wird das Lock wieder freigegeben. In meinem Beispiel ist das recht sinnfrei, aber es geht ja um's Prinzip.
Re: Klasse als Datenspeicher, die nur einmal existiert
Verfasst: Freitag 22. August 2014, 08:52
von jens
Re: Klasse als Datenspeicher, die nur einmal existiert
Verfasst: Freitag 22. August 2014, 10:06
von Sirius3
@jens: nein, er soll auf numpy aufbauen, um effizient große Mengen an Zahlen zu verwalten.