Seite 1 von 1

OOP Probleme, bitte um kurzen Denkanstoß...

Verfasst: Donnerstag 14. Februar 2008, 23:28
von akis.kapo

Code: Alles auswählen

#!/usr/bin/python

from __future__ import with_statement
from collections import defaultdict

IFS = '//'

class TagDB:
    def addtag(self, fname_obj, taglist):
        if isinstance(fname_obj, list):
            for fname in fname_obj:
                for tag in taglist:
                    self.filetag[fname].add(tag)
                    self.tagfile[tag].add(fname)
        else:
            for tag in taglist:
                self.filetag[fname_obj].add(tag)
                self.tagfile[tag].add(fname_obj)

    def deltag(self, fname_obj, taglist):
        if isinstance(fname_obj, list):
            for fname in fname_obj:
                for tag in taglist:
                    self.filetag[fname].discard(tag)
                    self.tagfile[tag].discard(fname)
        else:
            for tag in taglist:
                self.filetag[fname_obj].discard(tag)
                self.tagfile[tag].discard(fname_obj)

    def load_db_fromfile(self, dbfile):
        with open(dbfile, 'r') as tagdb:
            for line in tagdb:
                line = line.rsplit('\n')[0].split(IFS)
                fname = line[0]
                tags = list(set(line[1:]))
                self.addtag(fname, tags)


    def __init__(self, dbfile=None, filetag=None, tagfile=None):
        if dbfile is None:
            if filetag is None:
                self.filetag = defaultdict(set)
            else:
                self.filetag = filetag
            if tagfile is None:
                self.tagfile = defaultdict(set)
            else:
                self.tagfile = tagfile
        else:
            self.load_db_fromfile(self, dbfile)

### main programm ###

myTagDB = TagDB(dbfile='tagdb')

print myTagDB.filetag, myTagDB.tagfile
Tagfile:

Code: Alles auswählen

/path/to/myfile//tag1//tag2//tag3//test//abc//123
./yourfile//test//tag3//123
Kommandozeile:

Code: Alles auswählen

# ./tagger
Traceback (most recent call last):
  File "./tagger", line 54, in <module>
    myTagDB = TagDB(dbfile='tagdb')
  File "./tagger", line 50, in __init__
    self.load_db_fromfile(self, dbfile)
AttributeError: TagDB instance has no attribute 'load_db_fromfile'
Bitte mir kurz auf die Nuss haun.

Verfasst: Donnerstag 14. Februar 2008, 23:47
von Jan-Peer
Ruf doch in Zeile 51 die Methode load_db_fromfile ohne self als Parameter auf. Self wird immer implizit mit übergeben, wenn du self.methode aufrufst.

Ansonsten glaube ich aber, daß die Fehlermeldung und der Code nicht mehr ganz zusammenpassen. Kann es sein, daß du das "self." in Zeile 51 noch nachträglich eingefügt hast?

Verfasst: Freitag 15. Februar 2008, 00:01
von akis.kapo
Alle Methoden ausser load_db_fromfile gehen.

Ich verstehe nicht, wo der Unterschied liegt zw. dieser und den anderen Klasseneigenen Methoden.

Die letzte Version funktionierte perfekt:

Code: Alles auswählen

$ ./tagger
defaultdict(<type 'set'>, {'/myfile': set(['abc', 'tag1', 'tag2', 'tag3', '123', 'test']), './yourfile': set(['test', '123', 'tag3'])}) defaultdict(<type 'set'>, {'abc': set(['/myfile']), 'tag1': set(['/myfile']), 'tag2': set(['/myfile']), 'tag3': set(['/myfile', './yourfile']), '123': set(['/myfile', './yourfile']), 'test': set(['/myfile', './yourfile'])})
Und da sah der Code noch so aus:

Code: Alles auswählen

#!/usr/bin/python

from __future__ import with_statement
from collections import defaultdict

IFS = '//'

class TagDB:
    def addtag(self, fname_obj, taglist):
        if isinstance(fname_obj, list):
            for fname in fname_obj:
                for tag in taglist:
                    self.filetag[fname].add(tag)
                    self.tagfile[tag].add(fname)
        else:
            for tag in taglist:
                self.filetag[fname_obj].add(tag)
                self.tagfile[tag].add(fname_obj)

    def deltag(self, fname_obj, taglist):
        if isinstance(fname_obj, list):
            for fname in fname_obj:
                for tag in taglist:
                    self.filetag[fname].discard(tag)
                    self.tagfile[tag].discard(fname)
        else:
            for tag in taglist:
                self.filetag[fname_obj].discard(tag)
                self.tagfile[tag].discard(fname_obj)

    def __init__(self, dbfile=None, filetag=None, tagfile=None):
        if filetag is None:
            self.filetag = defaultdict(set)
        else:
            self.filetag = filetag
        if tagfile is None:
            self.tagfile = defaultdict(set)
        else:
            self.tagfile = tagfile

### main programm ###

myTagDB = TagDB()

with open('tagdb', 'r') as tagdb:
    for line in tagdb:
        line = line.rsplit('\n')[0].split(IFS)
        fname = line[0]
        tags = list(set(line[1:]))
        myTagDB.addtag(fname, tags)

print myTagDB.filetag, myTagDB.tagfile

Aber ich will unbedingt den Codeabschnitt im main programm als Klassenmethode wegkapseln.

Ich stelle mir vor, ein TagDB objekt so zu erstellen, dass ich einfach angebe, welches File er einlesen soll:

myTagDB = TagDB(dbfile='tagdb')

Das ist mein Ziel.

Verfasst: Freitag 15. Februar 2008, 07:14
von Jan-Peer
Wie gesagt: Du mußt das "self" in den Klammern beim Aufruf in Zeile 51 weglassen. Dann geht es auch. Und die Fehlermeldung, die du oben gepastet hast, ist sicherlich nicht die, die du mit dem obenstehenden Code erzeugst.

Verfasst: Freitag 15. Februar 2008, 08:53
von BlackJack
@akis.kapo: Ein wenig Kritik am Entwurf: Also für meinen Geschmack entscheiden die Funktionen alle viel zu sehr anhand der Argumente was sie letztendlich tun.

Die `isinstance()`-Aufrufe verhindern, dass `fname_obj` irgend ein anderes "iterable" als `list` sein darf. Das läuft dem "duck typing" zuwider. Alternativen wären, wenn schon Typtest, dann auf `basestring` zu testen und falls dem so ist `fname_obj` als einzelnes Element in eine Liste zu stecken und im weiteren davon aus zu gehen, dass `fname_obj` ein "iterable" von Dateinamen ist, oder explizit verschiedene Methoden zum hinzufügen eines Namens und vieler Namen zur Verfügung zu stellen.

Die `__init__` ist so auch reichlich unübersichtlich. Für alternative Konstruktoren eignen sich `staticmethod` und `classmethod`. Du hast ja selbst schon gesagt, Du möchtest das Laden als *Klassenmethode*, auch wenn Du vielleicht Instanzmethode gemeint hast. :-)

Ausserdem würde ich `filetag` und `tagfile` dort nicht einfach so ungeprüft annehmen. Da wird letztendlich der komplette innere Zustand von einem `TagDB`-Objekt von aussen geliefert und ein wesentlicher Punkt das ganze in einem Objekt zu kapseln ist ja, die Invariante, dass jeder Dateiname Tags zugeordnet ist und alle Tags zu Dateien gehören. Ich denke man gibt damit nach aussen zu viele Details über die Implementierung preis.

Alternativ-Entwurf: http://paste.pocoo.org/show/27840/

Verfasst: Freitag 15. Februar 2008, 12:04
von akis.kapo
@Blackjack.

Also ich hatte bereits im Hinterkopf, statt Listen auch sets() zu akzeptieren und wollte nur nach iterable überprüfen (was viel mehr als nur listen und sets akzeptieren würde), habe aber nicht rausgefunden, wie man das am geschicktesten macht in der Zeit.

Kannst du bitte erklären, was division macht? Ist da etwa das difference_update() drin? Weil in der libref sehe ich keine solche Funktion für dicts und im module index steht nix über division unter __future__. Wo kann ich nachlesen, was darin alles beinhaltet ist?

Ausserdem setzt du bei add() den tag2file[tag] = filename. Das verstehe ich nicht und ich habs umgeändert in add(), weil alle values bei mir sets() sind, siehe __init__. (Übersehen?)

An das strip() hätte ich natürlich auch denken können, mensch bin ich umständlich...

Hier mein Vorschlag mit deiner Version als Basis mit reduzierter Funktionalität (ohne add/remove_many, das mach ich viel, viel später...):

http://paste.pocoo.org/show/27873/

Verfasst: Freitag 15. Februar 2008, 12:12
von Leonidas
akis.kapo hat geschrieben:Kannst du bitte erklären, was division macht? Ist da etwa das difference_update() drin? Weil in der libref sehe ich keine solche Funktion für dicts und im module index steht nix über division unter __future__. Wo kann ich nachlesen, was darin alles beinhaltet ist?
``__future__`` ist auch kein eigentliches Modul sondern eher eine Interpreter-Direktive. In ``__future__`` sind Sachen drin, die erst in späteren Python-Versionen per Default eingeschaltet werden. Du kannst aber in ``Lib/__future__.py`` reingucken, was es so gibt. Einige der Sachen wie ``nested_scopes`` sind aber schon Standard.

Mehr zu ``__future__`` gibt es in PEP 236 zu ``division`` selbst in PEP 238.

Aber ganz ehrlich, das hättest du dir mit einer Internet-Suche auch aneignen können.

Verfasst: Freitag 15. Februar 2008, 12:18
von Jan-Peer
Leonidas, darf ich dich an das hier erinnern: http://www.python-forum.de/topic-13249.html ? :lol:

Verfasst: Freitag 15. Februar 2008, 12:18
von akis.kapo
@Leo

Das erklärt trotzdem nicht wo difference_updates() genau herkommt und wozu division überhaupt verwendet wird.

Ich habs mal ausm import einfach weggenommen und das Prog funzt genauso. :?

EDIT:

difference_update gehört dem set, nicht dem dict. Sorry.

Verfasst: Freitag 15. Februar 2008, 12:56
von BlackJack
@akis.kapo: Das mit der einfachen Zuweisung bei `tag2file` in `add()` war ein Fehler meinerseits. :oops:

Das `division` ist da reingerutscht, weil das Bestandteil von meinen Standardtemplate für den `__future__`-Import ist. Wird hier nicht benötigt.

Verfasst: Freitag 15. Februar 2008, 13:32
von akis.kapo
@BJ

Ok, das erklärt einiges. Ich dachte schon, dass das alles so sein __muss__ und vor lauter Falschannahmen stand ich kurz total aufm Schlauch (siehe Diskussion um difference_update()).

Meine aktuelle Version:

http://paste.pocoo.org/show/27888/

UPDATE:

http://paste.pocoo.org/show/27904/