Dynamische Variablenzuweisung

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
netdoobage
User
Beiträge: 12
Registriert: Donnerstag 1. März 2007, 15:13

Dynamische Variablenzuweisung

Beitragvon netdoobage » Donnerstag 1. März 2007, 15:46

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 :D), 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
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

Beitragvon HWK » Donnerstag 1. März 2007, 16:23

Hallo!
Entweder statt tupel1 etc. tupel[1] etc. verwenden oder aber mit

Code: Alles auswählen

setattr(self, 'tupel%i' % i, tupels[i - 1])
MfG
HWK
BlackJack

Beitragvon BlackJack » Donnerstag 1. März 2007, 16:28

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… ;-)
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

Beitragvon HWK » Donnerstag 1. März 2007, 16:45

@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
BlackJack

Beitragvon BlackJack » Donnerstag 1. März 2007, 16:56

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.
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

Beitragvon HWK » Donnerstag 1. März 2007, 21:01

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
PmanX
User
Beiträge: 123
Registriert: Donnerstag 25. Januar 2007, 13:50
Wohnort: Germany.BB.LOS
Kontaktdaten:

Beitragvon PmanX » Donnerstag 1. März 2007, 22:47

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.
BlackJack

Beitragvon BlackJack » Donnerstag 1. März 2007, 23:05

Das Problem ist *sehr* allgemein beschrieben. Welche Datenstruktur und ob (modul)global oder nicht kommt auf den Verwendungszweck an.
PmanX
User
Beiträge: 123
Registriert: Donnerstag 25. Januar 2007, 13:50
Wohnort: Germany.BB.LOS
Kontaktdaten:

Beitragvon PmanX » Donnerstag 1. März 2007, 23:43

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?
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5554
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Telfs (Tirol)
Kontaktdaten:

Beitragvon gerold » Freitag 2. März 2007, 01:05

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
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5554
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Telfs (Tirol)
Kontaktdaten:

Beitragvon gerold » Freitag 2. März 2007, 01:18

...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
:-)
http://halvar.at | Kleiner Bascom AVR Kurs

Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5554
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Telfs (Tirol)
Kontaktdaten:

Re: Dynamische Variablenzuweisung

Beitragvon gerold » Freitag 2. März 2007, 01:38

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:

Code: Alles auswählen

werte = []
werte.append(345)
werte.append(543)

Ü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
:-)
http://halvar.at | Kleiner Bascom AVR Kurs

Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
netdoobage
User
Beiträge: 12
Registriert: Donnerstag 1. März 2007, 15:13

Beitragvon netdoobage » Freitag 2. März 2007, 10:34

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
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5554
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Telfs (Tirol)
Kontaktdaten:

Beitragvon gerold » Freitag 2. März 2007, 11:34

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
:-)
http://halvar.at | Kleiner Bascom AVR Kurs

Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
netdoobage
User
Beiträge: 12
Registriert: Donnerstag 1. März 2007, 15:13

Beitragvon netdoobage » Freitag 2. März 2007, 13:55

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ß.:roll: Melde mich dann wieder...bis dahin ein schönes Wochenende

Vielen Dank für deine Mühen :D

Gruß netdoobage

Wer ist online?

Mitglieder in diesem Forum: Google [Bot]