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
Hyperion
Moderator
Beiträge: 7471
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Beitragvon Hyperion » Donnerstag 18. September 2008, 20:39

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 :) )
Benutzeravatar
jens
Moderator
Beiträge: 8458
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Beitragvon jens » Freitag 19. September 2008, 07:43

In prettify könntest du auch pprint.pformat nehmen: http://docs.python.org/dev/library/ppri ... nt.pformat

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
snafu
User
Beiträge: 5385
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Beitragvon snafu » Freitag 19. September 2008, 10:10

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?
Benutzeravatar
bwbg
User
Beiträge: 369
Registriert: Mittwoch 23. Januar 2008, 13:35

Beitragvon bwbg » Freitag 19. September 2008, 10:13

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)
Zuletzt geändert von bwbg am Freitag 19. September 2008, 10:16, insgesamt 1-mal geändert.
Benutzeravatar
jens
Moderator
Beiträge: 8458
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Beitragvon jens » Freitag 19. September 2008, 10:16

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) ;)

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
snafu
User
Beiträge: 5385
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Beitragvon snafu » Freitag 19. September 2008, 11:01

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: 5385
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Beitragvon snafu » Freitag 19. September 2008, 11:06

@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
Moderator
Beiträge: 8458
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Beitragvon jens » Freitag 19. September 2008, 11:10

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

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
snafu
User
Beiträge: 5385
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Beitragvon snafu » Freitag 19. September 2008, 11:23

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: 369
Registriert: Mittwoch 23. Januar 2008, 13:35

Beitragvon bwbg » Freitag 19. September 2008, 11:53

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: 5385
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Beitragvon snafu » Freitag 19. September 2008, 12:04

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: 369
Registriert: Mittwoch 23. Januar 2008, 13:35

Beitragvon bwbg » Freitag 19. September 2008, 13:11

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=]<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[/code]
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=][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[/code]

Ich hoffe die Umschreibung trifft es einigermaßen. Falls nicht, bitte entsprechend korrigieren.

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

Beitragvon snafu » Freitag 19. September 2008, 14:59

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: 369
Registriert: Mittwoch 23. Januar 2008, 13:35

Beitragvon bwbg » Freitag 19. September 2008, 18:27

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=]Mood (object)
-------------------------
name
text
time
mood
-------------------------
__init__(self, data : tuple) : None
__str__(self) : str
// returns a formated string with the objects data[/code]

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: 5385
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Beitragvon snafu » Samstag 20. September 2008, 12:32

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

Wer ist online?

Mitglieder in diesem Forum: Google [Bot], snafu, Sophus