Seite 1 von 2

Verfasst: Donnerstag 18. September 2008, 20:39
von Hyperion
Wo denn? Du hast doch schon wieder eine Fallunterscheidung drin (wobei ich das return nach dem if unübersichtlich finde! Ein explizites else ist da irgend wie schöner :) )

Verfasst: Freitag 19. September 2008, 07:43
von jens
In prettify könntest du auch pprint.pformat nehmen: http://docs.python.org/dev/library/ppri ... nt.pformat

Verfasst: Freitag 19. September 2008, 10:10
von snafu
Irgendwie check ich das mit den Klassen noch nicht richtig. Ich habe jetzt das hier gebastelt:

Code: Alles auswählen

class Generic(object):

    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 when setting tuples True.
        """
        keys = (elem[0] for elem in self.data)
        values = (elem[1:] for elem in self.data)
        return dict((key, value) for key, value in izip(keys, values))


class Moods(Generic):
    
    def __init__(self, data):
        self.data = data

    def prettify(self):
        """Return a human-readable string of all data"""
        
        data = self.data
        if type(data) is tuple:
            string, time, mood = data
            return '%s <%s>\n%s' % (string, time, mood)
        s = []
        for elem in data:
            name, string, time, mood = elem
            s.append('<%s> %s <%s>\n%s' % (name, string, time, mood))
        return '\n\n'.join(s)
Meine get_moods aus Datagetter erzeugt nun also ein Moods Objekt. Diese Klasse erbt von Generic die Methode get_dict (zumindest glaube ich das). Wenn ich jetzt sage:

Code: Alles auswählen

datagetter = pyspace.Datagetter()
[dann der login...]
moods = datagetter.get_moods()
moods.get_dict()
...dann kommt ein leeres Dict zurück. Verschiebe ich get_dict in die Klasse Moods, funktioniert es wunderbar. Wo liegt der Fehler?

Verfasst: Freitag 19. September 2008, 10:13
von bwbg

Code: Alles auswählen

# ...

class Printer(object):
    def printout(self):
        print '...Printer prints something...'

class OtherPrinter(object):
    def printout(self):
        print '...OtherPrinter prints something else...'

class MessagePrinter(object):
    def __init__(self, message):
        self.message = message
    def printout(self):
        print '...MessagePrinter prints "%s"' % (message,)

# ...

object_list = [Printer(), OtherPrinter(), MessagePrinter('Hallo Welt'), Printer()]

for o in object_list:
    o.printout()
Wenn ich das Problem richtig interpretiere, dann sollte oben genanntes Modell genau auf das Problem des OP passen. In C++ würde man eine (abstrakte) Basisklasse mit einer rein virtuellen Methode printout definieren und die Printer davon erben lassen - in Java wären das Interfaces. Soweit ich jetzt richtig informiert bin, ist das in Python nicht nötig.

Grüße... Heiko

EDIT: Einrückung korrigiert (böse Tabs)

Verfasst: Freitag 19. September 2008, 10:16
von jens

Code: Alles auswählen

import pprint

def prettify(data):
    return pprint.pformat(data)

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

if __name__ == "__main__":
    import doctest
    doctest.testmod(verbose=False)

    d = get_dict([('foo', 'bar', 'baz'), ('ham', 'egg', 'spam')])
    print prettify(d) # oder gleich pprint.pprint(d) ;)

Verfasst: Freitag 19. September 2008, 11:01
von snafu
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')]

Verfasst: Freitag 19. September 2008, 11:06
von snafu
@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.

Verfasst: Freitag 19. September 2008, 11:10
von jens
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

Verfasst: Freitag 19. September 2008, 11:23
von snafu
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... :(

Verfasst: Freitag 19. September 2008, 11:53
von bwbg
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)

Verfasst: Freitag 19. September 2008, 12:04
von snafu
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.

Verfasst: Freitag 19. September 2008, 13:11
von bwbg
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

Verfasst: Freitag 19. September 2008, 14:59
von snafu
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/

Verfasst: Freitag 19. September 2008, 18:27
von bwbg
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

Verfasst: Samstag 20. September 2008, 12:32
von snafu
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

Verfasst: Samstag 20. September 2008, 16:13
von bwbg
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

Verfasst: Samstag 20. September 2008, 16:48
von snafu
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/

Verfasst: Montag 22. September 2008, 12:26
von Y0Gi
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)

Verfasst: Montag 22. September 2008, 14:50
von snafu
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