Seite 1 von 2
Dynamische Variablenzuweisung
Verfasst: Donnerstag 1. März 2007, 15:46
von netdoobage
Erstmal ein freundliches Hallo!
Bin neu hier, habe euer Board aber schon ein paar mal mittels Suchfunktion genutzt. Das hat bisher immer ausgereicht (danke dafür
), allerdings hänge ich jetzt an einem Punkt an dem ich nicht weiter komme und auch die Suchfunktion bringt nicht den erwünschten Erfolg.
Der Hintergrund:
Ich habe eine Datenbankverbindung(PostgreSQL) und lese daraus mehrere Datensätze aus die mir dann als
eine Liste von Dictionarys ausgegeben wird.
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: iso-8859-1 -*- vatm
import sys, psycopg, os, time
conn=psycopg.connect('dbname=dbname host=x.x.x.x user=musterman password=passwd')
cursor = conn.cursor()
cursor.execute("select * from word_cudadtag")
colnames = [t[0] for t in cursor.description]
tupels = [dict(zip(colnames, tup)) for tup in cursor.fetchall()]
conn.close()
Da die einzelnen Datensätze relativ viele Felder haben und ich sie später weiterverarbeiten möchte(jeder Datensatz soll dann ein Objekt einer Klasse werden), sollte jeder Datensatz einer individuellen Variablen zugeordnet werden. Also Datensatz 1 -> tupel1; Datensatz 2 -> tupel2
Statisch ja kein Problem:
tupel1 = tupels[0]
tupel2 = tupels[1]
Problem:
Die Anzahl der Datensätze ist immer unterschiedlich, deshalb sollten die Variablen Namen auch dynamisch sein. Sprich pro Schleifendurchlauf sollte sich auch der Variablenname um eins erhöhen. Ich habe schon den ganzen Tag daran rumgedockert aber es funzt nicht. Habt ihr evtl. eine Idee
Besten Dank
netdoobage
Verfasst: Donnerstag 1. März 2007, 16:23
von HWK
Hallo!
Entweder statt tupel1 etc. tupel[1] etc. verwenden oder aber mit
MfG
HWK
Verfasst: Donnerstag 1. März 2007, 16:28
von BlackJack
Warum genau bist Du mit `tupels[0]`, `tupels[1]` und so weiter nicht zufrieden? An dynamische Namen kämst Du doch auch nur wieder dynamisch mit einem Index heran. Also genauso wie an die Listenelemente.
@HWK: Bitte ermuntere die Leute nicht auch noch so einen Schwachsinn zu machen…
Verfasst: Donnerstag 1. März 2007, 16:45
von HWK
@BlackJack: Schwachsinn ist vielleicht etwas zu heftig.
Die Diskussion gab es ja bereits mehrfach. Für Namen wie tupel1, tupel2 etc. finde ich das mit getattr, setattr auch nicht gut. Für Namen, die noch einen zusätzlichen Informationsgehalt liefern, halte ich das aber durchaus für sinnvoll. Ich habe z.B. ein Script geschrieben, in dem die Seiten eines Notebooks dynamisch aus einem Dict erstellt werden. Bei Drücken des OK-Buttons wird jeweils eine Methode aufgerufen, deren Namen sich aus dem key für die Notebook-Seite ergibt ('on_%s_click'). Durch Verwendung von getattr muss ich den Namen der Methode nicht im Dict abspeichern, trotzdem bleibt das Script lesbar.
Code: Alles auswählen
self.Bind(wx.EVT_BUTTON, getattr(self, 'on_%s_click' % page.lower()), btn)
MfG
HWK
Verfasst: Donnerstag 1. März 2007, 16:56
von BlackJack
Schwachsinn halte ich nicht für zu heftig. Dein anderes Beispiel hat einen entscheidenden Unterschied: Du schreibst die Namen auch statisch im Quelltext.
Wenn man aber *nur* dynamisch darauf zugreift, dann ist es unsinnig den jeweiligen Namensraum damit zu belästigen. Abgesehen davon, dass es komplizierter und damit schwerer verständlich ist als eine Liste oder ein Dictionary zu benutzen, ergibt sich auch noch die Gefahr von Namenskollisionen.
Verfasst: Donnerstag 1. März 2007, 21:01
von HWK
Aber das kann ja der Fragende selbst entscheiden. Warum sollte man ihm also die Informationen vorenthalten? Vielleicht will er seine "Variablen" gar nicht tupel1, tupel2 etc. nennen, sondern ihnen aussagekräftigere Namen geben? Seine Darstellung diente vielleicht nur der Vereinfachung. Deshalb sollte man doch mit allzu heftiger Kritik etwas zurückhaltender sein.
MfG
HWK
Verfasst: Donnerstag 1. März 2007, 22:47
von PmanX
Hallo,
IMHO hat netdoobage ein Problem, was mich auch schon längere Zeit beschäftigt.
Wie gestalte ich eine Klasse, in der ich eine größere Anzahl von Instanzen aufbewahren will?
Oder fülle ich eine globale Liste?
Gruß P.
Verfasst: Donnerstag 1. März 2007, 23:05
von BlackJack
Das Problem ist *sehr* allgemein beschrieben. Welche Datenstruktur und ob (modul)global oder nicht kommt auf den Verwendungszweck an.
Verfasst: Donnerstag 1. März 2007, 23:43
von PmanX
Ein kleines Beispiel:
Eine Tabelle aus einer Datenbank: Name, Vname, Ort
Eine Klasse Person.
Jede Zeile der DB in einer Instanz von Person ablegen und dieses Objekt in Tupel/Liste/Dict speichern.
Wie sollte diese Klasse aussehen?
Sollte die Liste nichts mit der Klasse zu tun haben?
Verfasst: Freitag 2. März 2007, 01:05
von gerold
PmanX hat geschrieben:Wie sollte diese Klasse aussehen?
Hi PmanX!
Die Frage sollte eher lauten: "Wie könnte..."
Hier ein kleines Beispiel, wie ich es gerne verwende, da man dann die Personen (Adressen) schön in Klassen drinnen hat und alles damit anstellen kann, was man will. Z.B. das Generieren eines Kurznamens usw.
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-
import psycopg2
from psycopg2.extras import DictConnection
class AddressList(list):
pass
class Address(object):
first_name = None
last_name = None
def __str__(self):
return "%s %s" % (self.first_name , self.last_name)
conn = psycopg2.connect(
user = "testuser", password = "testuser",
database = "testdb", connection_factory = DictConnection
)
cur = conn.cursor()
adrlist = AddressList()
cur.execute("select first_name, last_name from addresses")
for row in cur:
address = Address()
address.first_name = row["first_name"]
address.last_name = row["last_name"]
adrlist.append(address)
conn.close()
for address in adrlist:
print address # --> dafür wird __str__ heran gezogen
Man muss für die Adressliste natürlich keine eigene Klasse verwenden. Es hat aber Vorteile. Wenn man später etwas mit den Adressen tun möchte, dann kann man das in der Klasse AddressList direkt erledigen. Z.B.: ``adrlist.get_birthday_list()``.
mfg
Gerold
Verfasst: Freitag 2. März 2007, 01:18
von gerold
...und in dem speziellen Fall "psycopg2 mit DictConnection/DictCursor" kannst du deine Klasse z.B. so füllen:
Code: Alles auswählen
for row in cur:
address = Address()
for key, value in row.items():
setattr(address, key, value)
adrlist.append(address)
mfg
Gerold
Re: Dynamische Variablenzuweisung
Verfasst: Freitag 2. März 2007, 01:38
von gerold
netdoobage hat geschrieben:Sprich pro Schleifendurchlauf sollte sich auch der Variablenname um eins erhöhen.
Hallo netdoobage!
Werte in Attribute einer Klasseninstanz schreiben -- gut, warum nicht!
Werte einfach hochzählen und in Klassenattribute mit Name wie z.B. "wert_001", "wert_002", "wert_003" ist absolut ohne Sinn.
Wenn du durchnummerierte Werte brauchst, dann schreibe sie in eine Liste:
Über die Liste kannst du genauso gut, wenn nicht sogar vielfach besser auf die Werte zugreifen, da du diese wie gewohnt durchlaufen, sortieren, durchsuchen,... kannst. Wenn du diese Werte als Attribute in eine Klasseninstanz schreibst, dann musst du dir das alles selber programmieren.
Instanzattribute machen natürlich auch Sinn, aber nur dann wenn die Werte einen aussagekräftigen Namen haben.
Hier hat es Sinn, da mit den benannten Attributen ja auch im Sinne der Objektorientierten Programmierung etwas gemacht wird:
Code: Alles auswählen
class Person(object):
vorname = None
nachname = None
def get_name(self):
return "%s %s" % (self.vorname, self.nachname)
So nicht:
Code: Alles auswählen
class MessWerte(object):
wert_001 = None
wert_002 = None
wert_003 = None
wert_004 = None
wert_005 = None
So schon:
Code: Alles auswählen
class MessWerte(list):
def min_max(self):
return min(self), max(self)
werte = MessWerte()
werte.extend([1, 2, 3, 4, 5])
print werte
print werte.min_max()
mfg
Gerold
Verfasst: Freitag 2. März 2007, 10:34
von netdoobage
Hallo
Erstmal danke für eure Beiträge. Nachdem ich jetzt einigermaßen eure Vorschläge durchdacht habe, glaube ich (wenn ich das alles richtig verstanden habe - bin auch noch blutiger anfänger mit python), dass das Richtige so noch nicht dabei ist.
@ HWK:
Was ich bis jetzt über die setattr weiß ist, dass für Instanzen einer Klasse automatisch ein Dictionary angelegt wird das die Attribute festhält. Beim Attributzguriff wird in diesem Dictionary gesucht, dann in dem der
Klasse, dann in der Basisklasse. Mit setattr kann ich mir also eigene Attribut Zugriffsfunktionen definieren.
Ist das so richtig???
Falls ja bringt es mich leider nicht weiter denn ich möchte nicht die Attribute benennen so wie es auch gerold
angenommen hat (sorry das ich mein Problem nicht deutlicher beschrieben habe) sondern jeden Tupel zu einer Instanz meiner Basisklasse machen:
Code: Alles auswählen
class Basisklasse:
def __init__(self,tupel):
if tupel.has_key('dtagvertragsnummer'):
self.dtagvertragsnummer = tupel['dtagvertragsnummer']
self.verfahren = tupel['verfahren']
self.referenznummer = tupel['referenznummer']
if tupel.has_key('typ'):
self.typ = tupel['typ']
else:
pass
self.auftragid = tupel['auftragid']
if tupel.has_key('raum'):
self.raum = tupel['raum']
else:
pass
self.kunde = tupel['kunde']
if tupel.has_key('rufnummer'):
self.rufnummer = tupel['rufnummer']
else:
pass
self.strasse = tupel['strasse']
self.ort = tupel['ort']
self.zweidraht = tupel['zweidraht']
self.vierdraht = tupel['vierdraht']
self.hochbit = tupel['hochbit']
self.onkz = tupel['onkz']
self.asb = tupel['asb']
self.kvz = tupel['kvz']
self.ev = tupel['ev']
self.da = tupel['da']
self.hvtstrasse = tupel['hvtstrasse']
self.hvtort = tupel['hvtort']
self.hvtplz = tupel['hvtplz']
self.termin_unf = tupel['termin']
self.sachbearb_name = tupel['rkomname']
self.sachbearb_telnr = tupel['rkomtel']
else:
self.vertragnummer = tupel['vertragnummer']
self.kundennummer = tupel['kundennummer']
self.uevtnr = tupel['uevtnr']
self.portierung = tupel['portierung']
self.bereitstellung = tupel['bereitstellung']
self.typ = tupel['typ']
self.cuda_raum = tupel['cuda_raum']
self.cuda_name = tupel['cuda_name']
self.cuda_rufbisher = tupel['cuda_rufbisher']
self.cuda_strasse = tupel['cuda_strasse']
self.cuda_ort = tupel['cuda_ort']
self.cuda_dtag = tupel['cuda_dtag']
self.cuda_onkz = tupel['cuda_onkz']
self.cuda_asb = tupel['cuda_asb']
self.kvz = tupel['kvz']
self.cuda_endverschluss = tupel['cuda_endverschluss']
self.cuda_doppelader = tupel['cuda_doppelader']
self.cuda_bitrate = tupel['cuda_bitrate']
self.cuda_rkomname = tupel['cuda_rkomname']
self.cuda_rkomtel = tupel['cuda_rkomtel']
self.ticketsid = tupel['ticketsid']
self.ticketnummer = tupel['ticketnummer']
self.zwischenmeldung = tupel['zwischenmeldung']
self.reaktionsmeldung = tupel['reaktionsmeldung']
self.termin = tupel['termin']
self.geraeusch = tupel['geraeusch']
self.antwortetnicht = tupel['antwortetnicht']
self.keinankommenderruf = tupel['keinankommenderruf']
self.keineverstaendigung = tupel['keineverstaendigung']
self.einseitigeverstaendigung =
tupel['einseitigeverstaendigung']
self.sonstiges = tupel['sonstiges']
self.sonstigestext = tupel['sonstigestext']
self.tickets_hid = tupel['tickets_hid']
Damit ich die Objekte später weiterverarbeiten kann müssen die objekte einen eindeutigen Namen besitzen, da ich ja z.B. bei drei Datensätzen auch diese drei in einen Programmdurchlauf bearbeiten und verarbeitet möchte. PmanX hat das Problem ganz gut beschrieben:
Ein kleines Beispiel:
Eine Tabelle aus einer Datenbank: Name, Vname, Ort
Eine Klasse Person.
Jede Zeile der DB in einer
neuen Instanz von Person ablegen und den Instanzen eindeutige Namen geben. Das allerdings halt dynamisch entsprechend der Anzahl der Datensätze.
Bitte steinigt mich nicht wenn ich nicht alles so verstanden hab wie ihr es gemeint habt.
Vielen Dank. Gruß
netdoobage
Verfasst: Freitag 2. März 2007, 11:34
von gerold
netdoobage hat geschrieben:neuen Instanz von Person ablegen und den Instanzen eindeutige Namen geben. Das allerdings halt dynamisch entsprechend der Anzahl der Datensätze.
Hallo netdoobage!
Genau daran wird dein Konzept wahrscheinlich scheitern. Du solltest nicht Instanzen mit dynamisch vergebenen Namen anlegen, sondern die Instanzen in einer Liste oder in einem Dictionary ablegen.
- Ein Tupel ist ein unveränderbarer Kontainer für Elemente, auf die per Index (=Zahl) zugegriffen werden kann. Benenne Variablen nicht nach dem Typ der Variable, sondern nach dem Inhalt/Zweck. (kann schnell durchlaufen werden)
- Ein Dictionary ist ein veränderbarer Kontainer für Elemente, auf die mit einem Schlüssel (Zahl, Text, unveränderbare Objekte) zugegriffen werden kann. (ist sehr schnell, beim Zugriff auf ein Element, wenn man den Schlüssel weiß)
- Die Namen "Basisklasse" und "tupel" sind außerordendlich schlecht gewählt. Benenne die Klasse nach dem, was sie darstellen soll.
- Ich persönlich, würde die Daten nicht beim Erstellen der Klasseninstanz (nicht __init__) übergeben. Ich würde dafür eine eigene Methode schreiben, die ein Dictionary (nennen wir es mal datadict, da ich nicht weiß, was für Daten drinnen sind) übernimmt und die Daten in die Instanzvariablen schreibt.
z.B. so:
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-
class Vertrag(object):
antwortetnicht = None
asb = None
auftragid = None
bereitstellung = None
cuda_asb = None
cuda_bitrate = None
cuda_doppelader = None
cuda_dtag = None
cuda_endverschluss = None
cuda_name = None
cuda_onkz = None
cuda_ort = None
cuda_raum = None
cuda_rkomname = None
cuda_rkomtel = None
cuda_rufbisher = None
cuda_strasse = None
da = None
dtagvertragsnummer = None
einseitigeverstaendigung = None
ev = None
geraeusch = None
hochbit = None
hvtort = None
hvtplz = None
hvtstrasse = None
keinankommenderruf = None
keineverstaendigung = None
kunde = None
kundennummer = None
kvz = None
onkz = None
ort = None
portierung = None
raum = None
reaktionsmeldung = None
referenznummer = None
rufnummer = None
sachbearb_name = None
sachbearb_telnr = None
sonstiges = None
sonstigestext = None
strasse = None
termin = None
termin_unf = None
ticketnummer = None
tickets_hid = None
ticketsid = None
typ = None
uevtnr = None
verfahren = None
vertragnummer = None
vierdraht = None
zweidraht = None
zwischenmeldung = None
def import_datadict(self, datadict):
"""
Bekommt ein Dictionary mit Werten uebergeben, die importiert werden.
"""
translation = {
"rkomname": "sachbearb_name",
"rkomtel": "sachbearb_telnr"
}
if datadict.has_key('dtagvertragsnummer'):
translation["termin"] = "termin_unf"
for key, value in datadict.iteritems():
if key in translation:
setattr(self, translation[key], value)
else:
setattr(self, key, value)
def __str__(self):
"""
Stringausgabe
"""
if self.dtagvertragsnummer is not None:
s = "DTAGVertrag (%(dtagvertragsnummer)s):\n"
else:
s = "Vertrag (%(vertragnummer)s):\n"
if self.sonstiges is not None:
s += " %(sonstiges)s"
return s % self.__dict__
class Vertraege(dict):
"""
Das ist der Kontainer fuer die Vertraege
"""
def get_all_dtagvertraege(self, iterable = True):
"""
Gibt alle DTAG-Verträge als Generator zurück
"""
vertraege = (
self[vertrag] for vertrag in self
if self[vertrag].dtagvertragsnummer is not None
)
if iterable:
return vertraege
else:
return list(vertraege)
def main():
vertraege = Vertraege()
# Vertrag 1
datadict = {
"dtagvertragsnummer": "123456",
"sonstiges": "Wir sind gekommen um zu bleiben."
}
vertrag = Vertrag()
vertrag.import_datadict(datadict)
vertraege["123456"] = vertrag
# Vertrag 2
datadict = {
"vertragnummer": "65423",
"sonstiges": "Alles Walzer..."
}
vertrag = Vertrag()
vertrag.import_datadict(datadict)
vertraege["65423"] = vertrag
# Verträge anzeigen
for key, vertrag in vertraege.items():
print vertrag
print
print "------------------"
print
# Alle DTAG-Verträge anzeigen
for vertrag in vertraege.get_all_dtagvertraege():
print vertrag
if __name__ == "__main__":
main()
mfg
Gerold
Verfasst: Freitag 2. März 2007, 13:55
von netdoobage
Hallo Gerold!
Ein Tupel ist ein unveränderbarer Kontainer für Elemente, auf die per Index (=Zahl) zugegriffen werden kann. Benenne Variablen nicht nach dem Typ der Variable, sondern nach dem Inhalt/Zweck. (kann schnell durchlaufen werden)
tupels: (bei mir Mehrzahl von tupel) da aus der Datenbank mehrere Datensätze kommen - Datentyp ist 'list'
Die Namen "Basisklasse" und "tupel" sind außerordendlich schlecht gewählt. Benenne die Klasse nach dem, was sie darstellen soll.
Meine Basisklasse heißt Tupel(habe ich hier nur Basisklasse benannt das ihr wisst das es sich um diese handelt).
Tupel nenn ich sie weil ein Objekt dieser Klasse eigentlich ein Datensatz sein sollte. Ich weiß das ist vielleicht auch nicht das was dir vorschwebt aber ich fand die Namenwahl mit diesen Zusammenhängen nicht so schlecht.
Ich persönlich, würde die Daten nicht beim Erstellen der Klasseninstanz (nicht __init__) übergeben. Ich würde dafür eine eigene Methode schreiben, die ein Dictionary (nennen wir es mal datadict, da ich nicht weiß, was für Daten drinnen sind) übernimmt und die Daten in die Instanzvariablen schreibt.
Das ist gut, das könnte funktionieren. Brauche aber wieder ein paar Stunden um das zu testen. Wenn die DTAG nicht immer diese Spezifikationen hätte von denen man keinen Millimeter abweichen darf wäre das Problem auch nur halb so groß.
Melde mich dann wieder...bis dahin ein schönes Wochenende
Vielen Dank für deine Mühen
Gruß netdoobage
Verfasst: Samstag 3. März 2007, 00:21
von PmanX
Hallo gerold!
danke für die guten Beispiele.
Eine SQL-Abfrage braucht eher selten solche Objekte, da man ja in SQL 95% aller Filter, Berechnungen etc. in die Abfrage des DBMS einbauen kann.
Aber nehmen wir mal eine XML-Datei. Wenn ich diese einlesen(parsen) möchte, möchte ich diese auch als Objekt betrachten. Etwa so:
Code: Alles auswählen
class Address(object):
Adressen = {}
first_name = None
last_name = None
Wenn ich Deine Beispiele betrachte(ich habe sie noch nicht vollständig erfasst), sehe ich zwei Varianten.
Du erstellst zwei Klassen oder Du füllst einen Container außerhalb der Objekte. Es ist sicher eine Betrachtungsweise. Mir wäre es naheliegender, all dies in einer Klasse zu vereinen. Machbar sollte es doch sein.
Zu
netdoobage's Problem. Im DBMS sollte jede Tabelle einen sogenannten primary Key besitzen. Dieser kann doch vorzugsweise der Key im Dictionary der Tupel werden.
Gruß P.
Verfasst: Samstag 3. März 2007, 10:15
von BlackJack
Ich habe glaube ich immer noch nicht verstanden was Du willst. Ein `Adress`-Objekt, das gleichzeitig sowohl eine konkrete Adresse als auch alle Adressen ist? Das klingt gar nicht gut.
Verfasst: Sonntag 4. März 2007, 14:59
von PmanX
BlackJack hat geschrieben:Ich habe glaube ich immer noch nicht verstanden was Du willst. Ein `Adress`-Objekt, das gleichzeitig sowohl eine konkrete Adresse als auch alle Adressen ist? Das klingt gar nicht gut.
So gesehen hast Du vollkommen recht. In meiner Betrachtungsweise sehe ich wohl eher die Abfrage, die Datei .. als Objekt. Meine objektorientierte Sichtweise muß noch trainiert werden
Gruß P.
Verfasst: Dienstag 6. März 2007, 14:18
von netdoobage
Hallo @ all,
mit einem Dictionary die ganze Geschichte zu lösen klappt leider auch nicht. Da die Abfragen aus der Datenbank(können mehrmals die gleichen sein) die gleichen Feldnamen aufweisen und ich somit die Variablen immer wieder überschreibe(wenn ich sie aus dem Dictionary den Attributen zuweise). Mache jetzt erstmal an einer anderen Baustelle weiter...
Vielen Dank an alle die mir geholfen haben!
Beste Grüße
netdoobage
Verfasst: Donnerstag 8. März 2007, 13:26
von PmanX
netdoobage hat geschrieben:Hallo @ all,
mit einem Dictionary die ganze Geschichte zu lösen klappt leider auch nicht. Da die Abfragen aus der Datenbank(können mehrmals die gleichen sein) die gleichen Feldnamen aufweisen und ich somit die Variablen immer wieder überschreibe(wenn ich sie aus dem Dictionary den Attributen zuweise)
...
Wenn in der DB kein
eindeutiges(unique) Feld existiert, gehe ich von einem DesignProblem aus.
Gruß P.