Wie eigene Typen definieren?

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
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

jens hat geschrieben:

Code: Alles auswählen

def get_dict(data):
    """
    >>> get_dict([('foo', 'bar', 'baz'), ('ham', 'egg', 'spam')])
    {'foo': ('bar', 'baz'), 'ham': ('egg', 'spam')}
    """
    result = {}
    for item in data:
        result[item[0]] = item[1:]
    return result
Gut, das ist ne ganze Ecke unkomplizierter, aber in der Klasse Generic funktioniert es trotzdem nicht. Den Sinn von pformat kann ich leider nicht erkennen:

Code: Alles auswählen

>>> print pprint.pformat([('foo', 'bar', 'baz'), ('ham', 'egg', 'spam')])
[('foo', 'bar', 'baz'), ('ham', 'egg', 'spam')]
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@bwbg: Es geht schon um unterschiedliche Printer. Allerdings soll jeder Printer noch zusätzlich die Attribute von Generic besitzen. Und das bekomme ich nicht hin.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

pprint macht erst Sinn wenn man mehr Daten hat, z.b.:

Code: Alles auswählen

import pprint

daten = [
    {"jo": (1,2,3),1:"jep",2:"foo",3:"mehr text...",4:"noch mehr text..."},
    {"jo2": (1,2,3),1:"jep2",2:"foo",3:"mehr text...",4:"noch mehr text..."},
    {"jo3": (1,2,3),1:"jep3",2:"foo",3:"mehr text...",4:"noch mehr text..."},
]
pprint.pprint(daten)
Es Formatiert dir die Ausgabe:

Code: Alles auswählen

[{1: 'jep',
  2: 'foo',
  3: 'mehr text...',
  4: 'noch mehr text...',
  'jo': (1, 2, 3)},
 {1: 'jep2',
  2: 'foo',
  3: 'mehr text...',
  4: 'noch mehr text...',
  'jo2': (1, 2, 3)},
 {1: 'jep3',
  2: 'foo',
  3: 'mehr text...',
  4: 'noch mehr text...',
  'jo3': (1, 2, 3)}]
Ach, zum Umwandeln der Daten in ein dict: Wenn ein Key mehrmals vorkommt, werden die alten Werte überschrieben!
Man könnte sowas machen:

Code: Alles auswählen

def get_dict(data):
    result = {}
    for item in data:
        key = item[0]
        if key in result:
            raise AssertionError("Key %s ist doppelt!" % key)
        result[key] = item[1:]
    return result

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Was das Überschreiben eines Schlüssels angeht: In dem Fall würde ich wohl eher ne Warning werfen.

Pprint mag ja ganz nett sein, ist aber für prettify, so wie ich es mir vorstelle, nicht das richtige.

Und naja, eigentlich interessiert mich ja hauptsächlich dieses Problem beim Vererben der Klassen. ;)

//edit: Vor allem, eigentlich geht's ja:

Code: Alles auswählen

>>> class Generic(object):
	def get_dict(self):
	    return '%s is a dict now' % self.data

>>> class Moods(Generic):
	def __init__(self, data):
	    self.data = data
	def prettify(self):
	    return '%s is pretty now' % self.data

>>> moods = Moods('bla')
>>> moods.prettify()
'bla is pretty now'
>>> moods.get_dict()
'bla is a dict now'
Ich frag mich, was ich die ganze Zeit übersehe... :(
Zuletzt geändert von snafu am Freitag 19. September 2008, 11:53, insgesamt 1-mal geändert.
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Die Attribute von Generic könntest du einfach an die Printer vererben:

Code: Alles auswählen

class Generic(object):
    def __init__(self, message):
        self.message = message
        self.something = 'awesome'

class MessagePrinter(Generic):
    def __init__(self, message):
        Generic.__init__(self, message)
    def printout(self):
        print 'MessagePrinter prints "%s". This is %s.' % (self.message, self.something)
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

bwbg hat geschrieben:Die Attribute von Generic könntest du einfach an die Printer vererben:
Hab's genau so probiert aber es wird immer noch ein leeres Dict zurückgegeben. :(

Also die Funktion wird ja aufgerufen, aber die Daten kommen scheinbar nicht an.
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Wahrscheinlich sind wir unterschiedlicher Auffassung, was der Bedeutung von Attributen und Methoden zukommt.

Ich versuche mal zusammenzustellen, was ich im Moment an Informationen über dein Problem habe. Auf der Basis lässt sich der Faden hier vielleicht wieder gerade ziehen:

Du möchtest Daten, welcher immer aus aus einer Liste von Tupeln (immer 4 Elemente) möglichst benutzerfreundlich formatiert ausgeben.

Beispiel:

Code: Alles auswählen

<Obelix> Wildschweine sind was feines <12:30 Uhr> gut gelaunt
<Haudraufwienix> ... <15:12 Uhr> fischig
<Gaius Bonus> Viel zu weit weg von Aquarium <11:45 Uhr> niedergeschlagen
<Marcus Schmalzlockus> ... <13:34 Uhr> aufgedreht
Es gibt nun unterschiedliche Typen/Klassen, welche die Daten bereitstellen können. Nennen wir sie 'Roemer' und 'Gallier'. Je nach dem, welcher Typ gerade vorliegt, soll der einzelne "Eintrag" spezialisiert ausgegeben werden.

Beispiel:

Code: Alles auswählen

[Im Dorf: ]<Obelix> Wildschweine sind was feines <12:30 Uhr> gut gelaunt
[Im Dorf: ]<Haudraufwienix> ... <15:12 Uhr> fischig
[Klein Bonum: ]<Gaius Bonus> Viel zu weit weg von Aquarium <11:45 Uhr> niedergeschlagen
[Klein Bonum: ]<Marcus Schmalzlockus> ... <13:34 Uhr> aufgedreht
Ich hoffe die Umschreibung trifft es einigermaßen. Falls nicht, bitte entsprechend korrigieren.

Grüße... Heiko
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Der Fehler war, dass get_moods() die Daten nicht als Liste an Moods übergeben hat.

So, jetzt geht der Aufbau aber halbwegs, oder?

http://paste.pocoo.org/show/85687/
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Wenn es das tut, was es soll ... :wink:

Nach längerem hin- und herlesen:

Die Methoden in Generic greifen auf Attribute zu, die erst in einer Ableitung definiert werden. Das finde ich (persönlich) etwas konfus.

So wie ich das jetzt erkannt habe, werden eigentlich nur die Moods abgegriffen. Ein (Typ) 'Mood' besteht aus Name, Text, Uhrzeit und mood.
Zugleich soll 'Mood' in der Lage sein, eine möglichst lesbare Zeichenkette zu produzieren.

Mit fiele da folgendes Klassen-Diagramm (Pseudo-UML) zu ein:

Code: Alles auswählen

Mood (object)
-------------------------
name
text
time
mood
-------------------------
__init__(self, data : tuple) : None
__str__(self) : str
    // returns a formated string with the objects data

Code: Alles auswählen

class Mood(object):
    def __init__(self, data):
        assert isinstance(data, tuple) and len(data) == 4
        self.name = str(data[0])
        self.text = str(data[1])
        self.time = str(data[2])
        selt.mood = str(data[3])
        
    def __str__(self):
        return '<%s> %s <%s>\n%s' % (self.name, self.text, self.time, self.mood)
Wenn du die moods der einzelnen Personen via mechanize (damit kenne ich mich überhaupt nicht aus) ausliest, kannst du so direkt Mood-Instanzen erzeugen und in einer Liste speichern.

Pseudo:

Code: Alles auswählen

moods = []
while there_are_further_moods:
    mood_tuple = get_next_tuple_with_mechanize()
    moods.append(Mood(mood_tuple))
und später die Liste iterieren und die Mood-Instanzen ausgeben (oder sonst was damit anstellen)

Code: Alles auswählen

for mood in moods:
    print mood # ruft die __str__-Methode auf
Im finalen Aufruf könnte ich mir das Programm so vorstellen:

Code: Alles auswählen

dg = DataGetter()
moods = dg.get_moods()
for mood in moods:
    print mood
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hallo, bwbg!

Zunächst einmal vielen Dank, dass du dich mit meinem kompletten Code auseinander gesetzt hast. :)
So wie ich das jetzt erkannt habe, werden eigentlich nur die Moods abgegriffen.
Ja, nur ist das ja später nicht mehr der Fall. Z.B. gibt es bei einem der Informationskästen von Myspace die Sparte Profil-Updates. Hier können ganz unterschiedliche Sachen stehen wie "x hat y als Freund hinzugefügt" oder "x wurde in diesem Foto verlinkt: (Anzeige des Fotos im Miniformat, ich würde in dem Fall die Bild-URL ausgeben)" oder "x hat ein neues Foto zum Album z hinzugefügt: (siehe oben)" usw. Es kann also nicht immer '<%s> %s <%s>\n%s' für die Anzeige gelten und auch die Anzahl der Tuples variiert von Fall zu Fall. Daher müsste IMHO bereits durch Datagetter.get_*() eine Liste mit Objekten eines Typs erzeugt werden, wobei jedem Objekt dann halt der eigentliche Datensatz des Freundes übergeben wird. Wenn dieses Objekt aber eine bestimmte __str__-Mehode enthält, würde ich ja immer eine Ausgabe nach dem jeweiligen Schema erhalten. So etwas wie:

Code: Alles auswählen

moods = Generic(moods.get_moods()
print moods.get_data_by_name('Sven')
...würde also auch nur eine Ausgabe nach dem Schema liefern, weil get_moods() ja immer ein Moods Objekt zurückliefert. Natürlich könnten die get_*-()-Methoden einfach nur Listen ausliefern, auf die dann die abstrakt gehaltenen Methoden von Generic angewendet werden könnten, aber das verschönern aller Datensätze müsste von Typ zu Typ unterschiedlich geschehen. Etwa über Generic.prettify_moods(), Generic.prettify_updates() usw oder indem der User selber die entsprechende Klasse mit dem passenden __str__ für sein Objekt aufrufen soll. Und genau das will ich ja vermeiden. Ich möchte, dass alle get_*()-Methoden ein Objekt zurückliefern, welches prettify() *und* die Generic-Methoden besitzt. Der Anwender soll also nicht nochmal den Vermerk machen müssen "das Objekt, das gerade von get_moods() zurückkam, ist übrigens vom Typ Mood", wenn man so will.

Ich hoffe, mein Gedankengang ist einigermaßen klar geworden. :)

Gruß

Sebastian
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Programmbeschreibung (Annahme; ich kenne MySpace nicht):

Deine Seite, welche du interpretierst bieten verschiedene Informationen an:

- Freund hat einen anderen Freund hinzugefügt
- Freund hat ein Foto verlinkt
- Stimmung eines Freundes
...

Das Programm soll die Seite abfragen und die einzelnen Informationen ausgeben.

----

Folgende Klassen können aus der Beschreibung herausgelesen werden:

* Information (als Basis für die folgenden drei Klassen)
* FriendAppend
* PhotoLink
* FriendMood

Allen Informationsarten ist gemein, dass sie auf einen bestimmten Freund angewendet werden. Der Freund (Name des Freundes) sollte daher bereits als Attribut in der Klasse 'Information' vorgehalten werden.

Die Spezialisierungen haben jeweils eigene Attribute.

Vereinfachte Darstellung der Klassen:

Code: Alles auswählen

class Information(object):
    def __init__(self, friend):
        self.friend = friend
        
    def __str__(self):
        return '<%s>' % (self.friend,)

class FriendAppend(Information):
    def __init__(self, friend, who):
        Information.__init__(self, friend)
        self.who = who

    def __str__(self):
        return '<%s> added %s as a friend' % (self.friend, self.who)

class PhotoLink(Information):
    def __init__(self, friend, url):
        Information.__init__(self, friend)
        self.url = url

    def __str__(self):
        return '<%s> was linked in this photo: %s' % (self.friend, self.url)

# ...
Innerhalb Deines DataGetter-Objektes fragst du nun alle Informationen ab und erzeugst die spezialisierten Informationsobjekte. Diese packst du in eine Liste.

Mit dieser Liste kannst du dann entsprechend weiterarbeiten. Wenn du nun die Informationen zu einem bestimmten Freund suchst, iterierst du einfach durch diese Liste (oder verwendest List-Comprehension) und fragst das Attribut 'name' ab. Du kannst dann aber auch ein Dictionary erzeugen usw.

Der DataGetter ist in diesem Fall verantwortlich für die Unterscheidung der einzelnen Arten.

Ich hoffe, das hilft, dir meine Gedankengänge zu dem Thema ein bisschen darzulegen.

Grüße... Heiko
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Naja, ich übernehme überall die Original-Textstrings von der Myspace-Seite um zu gewährleisten, dass der Deutsche eine deutsche Ausgabe, der Spanier eine spanische Ausgabe usw hat, da ich annehme, dass die Myspace Seite in allen Sprachen gleich aufgebaut ist (müsste ich noch prüfen). Die Unterschiede sollen sein:

- prettify() <- Formatierung je nach Informationsart
- show_picture() <- wenn die Information Bilder beinhaltet, zum genauen Aufbau muss ich mir noch was überlegen

Und Informationen zu einem bestimmten Namen ermittle ich ja bereits mittels Dict. Schau dir mal get_dict() und get_data_by_name() an. ;)

Hier der derzeitige Code. get_updates() ist noch nicht verwendbar, da gerade erst angefangen.

http://paste.pocoo.org/show/85777/
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Ohne genau mitgelesen zu haben: MySpace hat doch eine API. Kannst du damit vielleicht besser arbeiten?


Und das hier

Code: Alles auswählen

def get_dict(data):
    keys = (elem[0] for elem in data)
    values = (elem[1:] for elem in data)
    return dict((key, value) for key, value in izip(keys, values))
lässt sich kompakt so schreiben:

Code: Alles auswählen

def get_dict(data):
    return dict((elem[0], elem[1:]) for elem in data)
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Y0Gi hat geschrieben:Ohne genau mitgelesen zu haben: MySpace hat doch eine API. Kannst du damit vielleicht besser arbeiten?
Bisher habe ich es so verstanden, dass man damit Anwendungen in Myspace integrieren kann. Was ich will, ist ja eigentlich das genaue Gegenteil, nämlich Myspace insbesondere auf langsamen Rechnern nutzbar zu machen, indem ich es unabhängig vom Browser machee. Bei nem Kollegen baut sich die Seite bspw. extrem langsam auf, wegen den ganzen JavaScripts, blinkenden Werberfeldern usw. obwohl er im Prinzip nur mal schnell gucken will ob ein neuer Kommentar oder sowas da ist.

Und genau da soll mein Projekt eben ansetzen. Ich denke langfristig auch an so Scherze wie die Überprüfung auf neue Einträge in einem bestimmten Intervall und wenn was da ist, wird der User benachrichtigt oder eine Funktion, die alle neuen Benachrichtigungen seit dem letzten Login anzeigt. Zunächst einmal möchte ich aber die ganzen Arten von Benachrichtigungen implementiert kriegen und dann kann's weitergehen. ;)

Und zu get_dict: Die derzeitige Version sieht eh schon anders aus:

Code: Alles auswählen

    def get_dict(self):
        """Return a dictionary of given data, expects a list with tuples

        For each tuple: Return first element as key and a tuple of remaining 
        elements as value.

        Example:
        In: [('foo', 'bar', 'baz'), ('ham', 'egg', 'spam')]
        Out: {'foo': ('bar', 'baz'), 'ham': ('egg', 'spam')}

        Intended to ease access on user related data.
        """
        dic = {}
        for elem in self.data:
            key = elem[0]
            if key in dic:
                msg = u'Renamed duplicate of key name "%s" to "%s_doublet"' \
                      % (key, key)
                warnings.warn(msg, category=DoubletKeyNameWarning)
                key = key + '_doublet'
            dic[key] = elem[1:]
        return dic 
Antworten