@classmethod ganz neu entdeckt (und Namespaces)

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

Hallo!

Ich bin mir im Moment ein wenig unsicher. Ich wollte mal ``@classmethod`` ausprobieren. Als Anwendung dafür habe ich den Zugriff auf einen in einer Textdatei gespeicherten Wert. Ich bin mir deshalb noch unsicher, da ich nicht weiß, ob man @classmethod überhaupt so verwenden soll.

Außerdem weiß ich jetzt auch nicht, ob ich den Klassennamen in diesem Fall so wie immer oder klein schreiben soll. Denn in dem Beispiel wird die Klasse ja nicht mehr wie gewohnt benutzt. -- Keine Instanz.

Ach ja, fast hätte ich es vergessen. Der Zweck dieser Übung ist, den Zugriff auf eine Textdatei, die nur einen Wert enthält, im gesamten Programm zu gewährleisten. Es soll verhindert werden, dass zwei Threads gleichzeitig die Datei zum Schreiben öffnen und sich dabei blockieren. Ich möchte die Zahl nicht im Speicher behalten, da diese kritisch ist und auch bei einem Absturz des Computers noch erhalten bleiben muss.

Code: Alles auswählen

import os
import threading

class letzte_tagab_id(object):
    
    filename = os.path.abspath("letzte_tagab_id.txt")
    lock = threading.Lock()
    
    @classmethod
    def get_id(cls):
        cls.lock.acquire()
        try:
            if os.path.isfile(cls.filename):
                f = file(cls.filename, "r")
                try:
                    return int(f.readline().strip())
                finally:
                    f.close()
            else:
                return 0
        finally:
            cls.lock.release()
    
    @classmethod
    def set_id(cls, new_id):
        cls.lock.acquire()
        try:
            f = file(cls.filename, "w")
            f.write(str(new_id))
            f.close()
        finally:
            cls.lock.release()

#test
print letzte_tagab_id.get_id()
letzte_tagab_id.set_id(15)
print letzte_tagab_id.get_id()
So oder so. Ich bin noch nicht ganz glücklich damit.

Mich würde interessieren, was ihr von dieser Art der Verwendung haltet und wie ihr es besser machen würdet.

lg
Gerold
:-)
Zuletzt geändert von gerold am Samstag 22. September 2007, 17:42, insgesamt 1-mal geändert.
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Ich würde auf jeden Fall das neue `with`-Statement benutzen - und zwar nicht nur für Filehandles, sondern auch für die Locks.


Klassenmethoden benutze ich persönlich, um aus einer Datenstruktur eine Instanz einer bestimmten Klasse zu erzeugen, ohne den entsprechenden Code in den Konstruktor einfügen zu müssen.

Beispiel:

Code: Alles auswählen

class Person(object):

    def __init__(self, name, age):
        self.name = name
        self.age = int(age)

    def __str__(self):
        return '<%s "%s", %d Jahre>' % (
            self.__class__.__name__, self.name, self.age)

    @classmethod
    def from_tuple(cls, t):
        return cls(*t)

t1 = ('Peter', 18)
peter = Person.from_tuple(t1)
Ich mag an Python besonders, dass man sich nicht wiederholen muss und einfach Refactoring betreiben kann: Dass der Konstruktor einen festen Namen hat, ist schon angenehm. Doch auch in der Klassenmethode kann ich eine Instanz erzeugen, ohne den Namen direkt zu verwenden. Als drittes Beispiel habe ich die String-Repräsentation eingefügt, die ich auch (wenn auch nicht ganz so kompakt) ohne Angabe des Klassennamens - aber mit selbigem als Rückgabewert - erzeugen kann. So bleibt nur eine einzige Stelle, an der der Name steht - und das ist (Achtung, Wortwitz) klasse (Alternativwortwitz: "...das ist super()").
poker
User
Beiträge: 146
Registriert: Donnerstag 20. September 2007, 21:44

Hi gerold,

Meiner Meinung sollte man Klassen nicht als Namensräume missbrauchen. Dafür gibt es Module die letztendliche auch nur Objekte sind; Singeltons wenn man so will ;)

Für dein Problem würde sich eine Singelton-Pattern (in Form einer Klasse) oder ein Borg-Pattern anbieten (Von letzteren würde ich abstand nehmen da absolut schwachsinnig-> Speicher, etc. genaueres kann man im Cookbook lesen).

Ein "sauberes" Singelton könnte so aussehen:

Code: Alles auswählen

class EggsAndSpamm(object):
    def __new__(cls, *args, **kwargs):
        if not "_instance" in cls.__dict__:
            cls._instance = object.__new__(cls)
        return cls._instance

    def meth(self):
        pass
Eine Sache gibt es aber die zu beachten ist (Auf die Alex Martelli in seinem Vortag nicht gekommen ist :roll:):
Wenn die Vererbung **sauber** klappen soll, muss ``if not "_instance" in cls.__dict__:`` anstatt von ``hasattr(cls, "_instance")`` benutzt werden!

Letztendlich ist das verhalten das gleiche wie beim Mono-State-Pattern (Borg) mit dem Unterschied das wirklich nur eine Instanz erzeugt wird und nicht 12345677**12348563774 wie beim Borg-Pattern, die alle den gleichen Zustand haben :roll: :twisted:

mfg
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

poker hat geschrieben:Meiner Meinung sollte man Klassen nicht als Namensräume missbrauchen. Dafür gibt es Module die letztendliche auch nur Objekte sind
Hallo poker!

Ich möchte die Diskussion hier nicht abbrechen, aber dazu muss ich einfach was sagen.

Du glaubst also, dass es einfacher ist, ein Modul mit nur 20 oder 30 Zeilen Code anzulegen und dieses in das Hauptmodul zu importieren, als diese paar Zeilen in einer Klasse im selben Modul abzulegen?

NEIN!!!!!!!!!!!!!!!!!!!!!!!!

Nur weil das von ein paar Leuten vorgesagt wird, ist es noch lange nicht praktikabel.

Wer sagt denn, dass Klassen nicht auch als kleine Container zur Codegliederung verwendet werden dürfen?

Ich weigere mich, meine Programme komplizierter zu machen als sie sein müssen.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
BlackJack

Hihi, ich habe extra nicht gleich geantwortet das man so was Blödsinniges machen kann wenn man auch nichts dagegen hat Klassen als Namensräume für einfache Funktionen zu verwenden. :twisted:

Aber mal ernsthaft: So etwas kommt doch in der Regel in grösseren Programmen vor wo mehrere Module dieses "Singleton" importieren, und da ist ein eigenes Modul schon die sauberere Lösung.

Ausserdem braucht man für ein eigenes Modul nicht zwingend eine Datei:

Code: Alles auswählen

In [42]: spam = types.ModuleType('spam')

In [43]: spam
Out[43]: <module 'spam' (built-in)>
:-D
poker
User
Beiträge: 146
Registriert: Donnerstag 20. September 2007, 21:44

Hallo gerold,

Deshlab habe ich dir ja auch zu einem richtigen Singelton geraten! ;) Wie das umzusetzen ist, habe ich ja schon gepostet. Das ist viel(!!) besser als eine Klasse als Namensraum mit statischen Methoden zu **missbrauchen**, glaubs mir das hat nur nachteile :)

BTW:
Nur weil das von ein paar Leuten vorgesagt wird, ist es noch lange nicht praktikabel.
Damit kannst du mich überhaupt nicht meinen ;)

Meine Meinung resultiert nicht daraus das andere Leute mir sagen was richtig und falsch ist, sondern durch eigene Erfahrung! Z.B. schwören ja soviele auf Alex Martelli`s Borg-Pattern. Für mich ist das totaler Dreck und ein Anti-Pattern! Wozu brauch ich 10000000000 Objekte mit gleichen zustand? Nur um ein Singelton zu forcieren?! :roll: Macht sich den irgendeiner über den Speicher und Overhead Gedanken? Ne dann lieber ein richtiges wie oben angezeigt, oder zu 99% lieber gleich ein Modul :)

Also wie du siehst zieht dein Spruch "Nur weil das von ein paar Leuten vorgesagt wird" hier nicht und kann ich garnicht meinen, sonst würde ich schon längs meinen RAM mit dem Borg-Pattern assimilieren :D ;)

mfg
poker
User
Beiträge: 146
Registriert: Donnerstag 20. September 2007, 21:44

BlackJack hat geschrieben:

Code: Alles auswählen

In [42]: spam = types.ModuleType('spam')

In [43]: spam
Out[43]: <module 'spam' (built-in)>
:-D
Try this ;)

Code: Alles auswählen

In [1]: import sys
In [2]: spam = type(sys)("spam")
In [3]: spam
Out[3]: <module 'spam' (built-in)>
In [4]:
Ist IMO noch sauberer :D ``types`` (oder war das ``new``?) wird ehe in py3k gekillt.

mfg
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Hallo BlackJack!
BlackJack hat geschrieben:So etwas kommt doch in der Regel in grösseren Programmen vor wo mehrere Module dieses "Singleton" importieren, und da ist ein eigenes Modul schon die sauberere Lösung.
Das ist hier aber nicht der Fall.
- Kleines Programm < 1000 Zeilen.
- Nur ein Modul und soll auch nicht mehr werden.
- Eine Zahl, die in eine Textdatei ausgelagert werden muss.
- Es soll ein einfacher Zugriff auf die Zahl geschaffen werden. Lesend und schreibend.
BlackJack hat geschrieben:Ausserdem braucht man für ein eigenes Modul nicht zwingend eine Datei
Ich höre. Wie würdest du diesen Fall implementieren, wenn du diese paar Zeilen Code nicht auf ein eigenes Modul auslagern sollst und nicht einfach lose zwei Funktionen irgendwo hin pappen sollst.

Es ist noch nie vorgekommen, dass ich so ein Konstrukt **nur** für den Zugriff auf einen Wert gebraucht hatte. Meist hatte ich viele Einstellungen. Und diese waren über eine Klasse "Settings" zugänglich. Diese Klasse wurde meist global instantiiert und über die Klasseninstanz "settings" zugänglich gemacht.

Code: Alles auswählen

class Settings(object):
    def __init__(...):
        # Einstellungen einlesen 
        self.einstellung1 = "asdf"
        self.einstellung2 = "fffff"

# GLOBAL
settings = Settings()
...
Das Auslagern macht in vielen Programmen keinen Sinn. Auslagern, nur des Auslagerns wegen. Da muss man sich ja auf den Kopf greifen.

Statt diesem, oben gezeigten Konstrukt -- was würdest du (und wie) verwenden? Und bitte nicht in ein anderes Modul auslagern. Dann müssten auch Kleinstprogramme mit wenigen tausend Zeilen aus mehreren Modulen bestehen. -- Nur des Auslagerns wegen...

Ich werde sachlich bleiben. -- Aber bitte, keine Lösung die das Programm komplizierter macht als oben gezeigtes Konstrukt.

Die Sache mit dem Modul ohne zusätzliche Datei interessiert mich natürlich sehr. ;-) Wie würde das in diesem Fall funktionieren?

lg
Gerold
:-)

PS: In der Klasse "Settings" würde ich für das ursprüngliche Problem natürlich Properties verwenden.
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
poker
User
Beiträge: 146
Registriert: Donnerstag 20. September 2007, 21:44

Gerold, schade das du dir keine Mühe machen willst wenigstens meine Vorschlag auszuprobieren, geschweigen den die Posts richtig zu Lesen :roll:

Vorschläge wurden genug genannt: Singleton, Borg, ``types(sys)("blubb")``
Bin raus...
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

poker hat geschrieben:Ein "sauberes" Singelton könnte so aussehen:
Hallo poker!

Code: Alles auswählen

class EggsAndSpamm(object):
    def __new__(cls, *args, **kwargs):
        if not "_instance" in cls.__dict__:
            cls._instance = object.__new__(cls)
        return cls._instance

    def meth(self):
        return "Hallo"


print EggsAndSpamm.meth()
# TypeError: unbound method meth() must be called with EggsAndSpamm 
# instance as first argument (got nothing instead)
Kein greifbarer Gewinn gegenüber der normalen, global instantiierten Klasse. Entweder muss ich jedes mal wenn ich darauf zugreifen möchte oder einmal global die Klasse instantiieren. Ich muss die Klasseninstanz also doch wieder selber erstellen. Ich werde nur daran gehindert, die Klasse mehrfach zu instantiieren. Oder was habe ich falsch verstanden?

mfg
Gerold
:-)
Zuletzt geändert von gerold am Freitag 21. September 2007, 20:24, insgesamt 1-mal geändert.
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: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

poker hat geschrieben:Gerold, schade das du dir keine Mühe machen willst wenigstens meine Vorschlag auszuprobieren, geschweigen den die Posts richtig zu Lesen :roll:
Hallo poker!

Wie soll ich das jetzt machen?
Try this ;)

Code: Alles auswählen

In [1]: import sys
In [2]: spam = type(sys)("spam")
In [3]: spam
Out[3]: <module 'spam' (built-in)>
In [4]:
Einfach lose zwei Funktionen irgendwohin stellen und an "spam" binden? --> Was gewinne ich dadurch? Ich suche etwas um Ordnung im Quellcode zu schaffen und nicht um zwei lose Funktionen an ein Objekt zu binden. Und hier wieder die gleiche Frage: Was habe ich falsch verstanden?

mfg
Gerold
:-)

PS: Du steigst ja schneller aus Themen aus als ich. ;-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
poker
User
Beiträge: 146
Registriert: Donnerstag 20. September 2007, 21:44

gerold hat geschrieben: Kein greifbarer Gewinn gegenüber der normalen, global instantiierten Klasse. Ich muss die Klasseninstanz also doch wieder selber erstellen. Entweder, jedes mal wenn ich darauf zugreifen möchte oder einmal global. Oder was habe ich falsch verstanden?
Ja, falsch verstanden. Du musst es selbstverständlich so aufrufen ``EggsAndSpamm().meth()``.
Das mit der "doch wieder selber erstellen" ist nicht ganz richtig. Es ist Singelton. Bei ersten Aufruf von ``EggsAndSpamm()`` wird die durch __new__ erzeugt Instanz in ``_instance`` gespeichert. Bei jeden weiteren Aufruf von ``EggsAndSpamm()` wird keine neue erstellt, sondern die bereits erzeugt wider zurückgegeben :)

``print id(EggsAndSpamm()), id(EggsAndSpamm())`` => 10400816 10400816
Kein greifbarer Gewinn gegenüber der normalen, global instantiierten Klasse.
Doch es gibt ein greifbaren Gewinn. Du kannst __iter__ implementieren und benutzen, das du bei einer nicht instantiierten Klasse nicht nutzen kannst. Du kannst in Grunde alles machen wie mit einer normalen Klassen-Instanz. Das alles geht nicht wenn man die Klasse direkt als Namensraum nutzt (Ok, mit Modulen geht es auch nicht).

Falls du diese Funktionalität nicht benötigst, kannst du auch folgendes machen:

Code: Alles auswählen

In [1]: import sys
In [2]: spam = type(sys)("spam")
In [3]: spam
Out[3]: <module 'spam' (built-in)>
In [4]: cfg = type(sys)("cfg")
In [5]: cfg.foo = "Moin"
In [6]: cfg
Out[6]: <module 'cfg' (built-in)>
In [7]: dir(cfg)
Out[7]: ['__doc__', '__name__', 'foo']
Daran kannst du auch Funktionen binden, eben alles was mit einen Modul geht. Meiner Erfahrung und Meinung nach sind beide Lösung die saubersten. Wie gesagt das ist nur **meine Meinung**. Ich habe lange Zeit auch Klassen als Namensräume genutzt und bin dann irgendwann auf "richtige" Singeltons umgestiegen, nach dem ich viele nachteile hatte (Auserdem sind wir ja nicht hier bei Java SCNR;))

Mein Fazit:
Wenn ich eine einmalige Instanz von etwas brauche für globalen Zugriff (Settings, etc), der mir aber auch die Freiheit geben soll __iter__, __eq__, etc zu nutzen => Singelton in form eine Klasse mit obigen __new__-code.

Wenn ich aber nur einen globalen Namensraum brauche der einmalig ist (auch für Settings, etc) => Ein Modul oder ``type(sys)("eggs")``.

``@classmethod`` nutze ich nur für alternative Konstruktoren, damit man das object noch anders erzeugen kann.

mfg

P.S.: Aber letztendlich ist das immer die Entscheidung des anderen. Wenn du glaubst das du das in Form einer Klasse mit ``@classmeth`` brauchst und dich damit wohler fühlst, ist das O.K. und machs dann.

Letztendlich ist die "reine" Lehre nur soviel Wert bis man auf deren Grenzen stößt, das aber in dem Fall halt nicht gegeben ist. Deshalb empfehlen wir dir auch de anderen Lösungen weil sie "sauberer" und "pythonischer" sind.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

poker hat geschrieben:Wenn du glaubst das du das in Form einer Klasse mit ``@classmeth`` brauchst und dich damit wohler fühlst, ist das O.K. und machs dann.
Hallo poker!
Hallo BlackJack!

Das ist es ja! Ich finde @classmethod nicht so intuitiv wie das Beispiel in diesem Beitrag http://www.python-forum.de/post-78121.html#78121

Singleton's halte ich für den ganz oben genannten Fall für überzogen. Es geht ja nur darum, für das gesamte Modul zwei Funktionen in einem Namensraum zur Verfügung zu stellen. Sie gehören logisch zusammen. Und weil ich gerne in abeschlossenen Einheiten (Containern) denke, wäre es, für mich persönlich, eine große Hilfe, wenn diese beiden Funktionen nicht irgendwo herum liegen, sondern sich in einem abgeschlossenen Bereich (z.B. eine Klasse) befinden.

Das Binden der beiden Funktionen an ein intern erstelltes Modul-Objekt ist ja gar nicht mal so schlecht. Jetzt fehlt noch die *optische* Kapselung. -- Der Quellcode darf seine Eleganz nicht verlieren. Dinge die zusammen gehören, müssen bei mir optisch zusammen sein. Ich verwende viel Zeit darin, mir die einfachen, intuitiven, leicht reproduzierbaren Möglichkeiten von Python raus zu picken und in meinen Programmen zu verwenden.

Jetzt ist ein Experiment mit @classmethod an der Reihe gewesen. Bis jetzt ist mein Fazit so: Ich brauche für jede Methode einen zusätzlichen Dekorator und weiß nicht, ob ich die Klasse groß oder klein schreiben soll. Das ist bis jetzt noch kein Gewinn gegenüber der alten Methode, in der ich die Klasse als Namensraum verwendet habe.

Wisst ihr, was ich eigentlich suche?

So etwas, oder etwas ähnliches. Also ein Namespace oder ein "Modul" im Modul:

Code: Alles auswählen

namespace letzte_tagab_id:
    filename = "xyxcv"
    
    def get_id():
        return "asdf"

    def set_id(new_id):
        ...

letzte_tagab_id.set_id(10)
print letzte_tagab_id.get_id()
Bis jetzt kommt das hier immer noch recht nahe an das was ich haben möchte ran:

Code: Alles auswählen

class Settings(object):
    def __init__(...):
        # Einstellungen einlesen
        self.einstellung1 = "asdf"
        self.einstellung2 = "fffff"

# GLOBAL
settings = Settings()
...
mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
poker
User
Beiträge: 146
Registriert: Donnerstag 20. September 2007, 21:44

Kurz für mich zusammengefasst:
Dir geht es also nur darum nur ein Modul zu haben = dein Programm. Nun suchst du nach einer Möglichkeit zusammengehöriges Optisch zu strukturieren und darüber zuzugreifen.

Ich hab zwar dein Problem (hoffentlich) nun erfasst aber es will sich mir nicht so ganz erschließen weshalb es ein Problem darstellt? Warum packst du es nicht einfach in der Klasse und erzeugst eine Instanz. Der Rest vom Modul greift dann auf die Instanz zu :) Ich weiß, global's sind verpönt aber hey, es ist ja nicht Modulweit sondern nur in dem einen und es werden eben Daten bereitgestellt die Global verfügbar sein müssen! Punkt.
Kurz und Schmerzlos:

Code: Alles auswählen

# Global Settings
class Config(object):
    pass

# Other stuff
class CheesShop(object):
    pass

# Globals
cfg = Config()
chees = CheesShop()

# do something
Nun kann der Rest eben darauf zugreifen.

Falls das auch nicht O.K. ist kannst du folgendes Fake-Singelton-Pattern nutzen :D

Code: Alles auswählen

# Global Settings
class cfg(object):
    pass
cfg = cfg()
# Other stuff
class chees(object):
    pass
chees = chees()

# do something
Wird auch hin und wider gern genommen, aber gefällt mir persönlich nicht. Aber damit hast du quasi auch Singeltons :D

mfg
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Hallo!

Also ganz ehrlich. Und ich schäme mich auch nicht dafür. :-) Derzeit würde ich (ohne Properties und ohne Rücksicht auf "set" im Quellcode) das Beispielproblem immer noch so angehen:

Code: Alles auswählen

import os
import threading


class LetzteTagabId(object):

    filename = os.path.abspath("letzte_tagab_id.txt")
    _lock = threading.Lock()
    
    def get(self):
        LetzteTagabId._lock.acquire()
        try:
            if os.path.isfile(LetzteTagabId.filename):
                f = file(LetzteTagabId.filename, "r")
                try:
                    return int(f.readline().strip())
                finally:
                    f.close()
            else:
                return 0
        finally:
            LetzteTagabId._lock.release()
    
    def set(self, new_id):
        LetzteTagabId._lock.acquire()
        try:
            f = file(LetzteTagabId.filename, "w")
            f.write(str(new_id))
            f.close()
        finally:
            LetzteTagabId._lock.release()

# GLOBAL
letzte_tagab_id = LetzteTagabId()

#test
print letzte_tagab_id.get()
letzte_tagab_id.set(25)
print letzte_tagab_id.get()
Unverbesserlich? Nein. Ich bin für schönere Lösungen offen. Sie sollten nur nicht den Code zerreißen. Ich denke sehr viel über Sinn und Unsinn vieler Codekonstrukte nach (wieder mal keine Freundin ;-) ) und wenn mich etwas überzeugt, dann setze ich es in Zukunft in meinen Programmen ein. Das ist auch der einzige Grund für diesen Topic. Ich will herausfinden, ob es schönere Möglichkeiten gibt.

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
poker
User
Beiträge: 146
Registriert: Donnerstag 20. September 2007, 21:44

Gerold, ehrlich das doch IMO voll O.K. :) Wenn man global's braucht dann braucht man sie halt :) Viele integrierten Debug-Kits oder Logger machen es auch einfach so (Obwohl ich mir diesbezüglich eine real AOP in Python wünschen würde.).

BTW: Den get und set würde ich aber dennoch ein property spendieren; fühlt sich angenehmer an.

mfg
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

poker hat geschrieben:Nun suchst du nach einer Möglichkeit zusammengehöriges Optisch zu strukturieren und darüber zuzugreifen. Ich hab zwar dein Problem (hoffentlich) nun erfasst aber es will sich mir nicht so ganz erschließen weshalb es ein Problem darstellt? Warum packst du es nicht einfach in der Klasse und erzeugst eine Instanz.
Hallo poker!

:P Das ist es ja. -- Das war nie ein Problem für mich und es war und ist DIE Lösung, die ich seit ich Python kenne so einsetze.

...bis jemand schrieb, dass man seiner Meinung nach Klassen nicht für so etwas einsetzen soll. Methoden, die nicht auf ein Attribut der Klasseninstanz zugreifen müssen, gehören nicht in eine Klasse. --- Jetzt bin ich in der Phase, diese Aussage zu überprüfen und meine Methode gegenüber anderen Alternativen abzuwiegen. Ich habe jetzt schon einiges durchprobiert: auslagern in Module, statische Methoden, Klassenmethoden,... Und das Binden von Funktionen an ein intern erstelltes Modul-Objekt wird mich die nächsten Tage auch noch beschäftigen. Es genügt nicht, einfach nur ein kurzes Beispiel auszuprobieren. Solche Dinge müssen sich in der Praxis bewähren und zum Stil eines Programmierers passen.

Wir sind jetzt zwar komplett von der ursprünglichen Fragestellung weg gekommen, aber das macht ja nichts.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
poker
User
Beiträge: 146
Registriert: Donnerstag 20. September 2007, 21:44

Hab den Code vorhin nur kurz überfolgen und dabei ist mir garnicht aufgefallen, dass deine Klasse keinerlei Zustände kennt :D Ok, so mancher Logger kennt auch keine ;)
gerold hat geschrieben:Methoden, die nicht auf ein Attribut der Klasseninstanz zugreifen müssen, gehören nicht in eine Klasse.
Ja :D Das ist eben die "reine" Lehre. An für sich ist das ja auch richtig. Ich hole mal ein wenig aus, da wir ja schon ein wenig OT sind:

Eine Klasse mit einer Ansammlung von Methoden (=Verhalten) ohne Attribute (=Zustände) auf die sie Zugreifen, bedeutet nach der OOP Lehre => Ein Objekt mit verhalten ohne Zustand. Wie kann aber ein Entität Verhalten ohne Zustände haben?! Das ist paradox. Selbst das primitivste Lebewesen hat Zustände, die erst ein gewisses Verhalten begünstigen. Entfernen wir uns davon und gehen über zu Objekten wie z.B. ein Auto, trifft das auch darauf zu: Ohne Benzin (=Zustand) keine Fortbewegung (=Verhalten).

Deshalb wird OOP ad absurdum geführt, wenn man das dahinterliegende Konzept (Modellierung von Objekten die eine Einheit aus Zuständen und Verhalten bilden) dafür benutzt um einfach Funktionen darin zu kapseln ohne das die Klasse irgend einen Zustand kennt, der Verändert werden kann oder in Abhängigkeit dessen das Verhalten gesteuert werden kann.

Bestes Beispiel dafür ist Java, das als rein OOP Sprache entwickelt wurde. Irgendwann hat man doch gemerkt, das man ganz ohne Funktionen nicht auskommt. Was haben die daraufhin gemacht? Klar, statics und classmethods eingeführt. Damit wurden sie ein stückweiter weniger dem angestrebten "Ideal" gerecht => Ganz ohne geht es dann doch nicht ;) Und durch die "Integrierung" von AOP haben die sich dann gänzlich von dem "Ideal" OOP entfernt.

Worauf ich nun letztendlich raus will ist, das ein oder einige wenige erkorenen Lösungen für ein Problem nur soviel Wert sind, bis man an die grenzen dessen stößt. Ab da an muss man andere Wege beschreiten und Konvention den Rücken kehren um neue Lösungen zu finden.

Bei dir stoßen die Lösungen, nun reine zusammengehörige Funktionen zu benutzen die im module space kleben, in so fern an ihre Grenzen, als das du dadurch für dein empfinden ein zu sehr fragmentierten Code erhälst. Du suchst für dich einen Weg zusammengehöriges optisch durch Einrückungen zu strukturieren. Wenn du dabei ein besseres Gefühl hast, dann wird dich da keiner hindern. -- Vielleicht wäre es in der Tat nicht schlecht ein namesapce-statement in Python einzuführen, damit man die Möglichkeit hat zusammengehörige Funktionen optisch zu gruppieren.

Einer meiner würde es so machen wenn es wirklich nur Funktionen sind ohne dazugehörige Zustände:

Code: Alles auswählen

## Funktions for getting and setting day at id's
def get_last_day_at_id():
    pass

def set_last_day_at_id(new_id):
    pass

## other stuff
Die ## gefolgt von einer kurzen Info sind für mich genug optisch Abgrenzung und Strukturierung.

mfg

edit: typos & struct
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

poker hat geschrieben:Vielleicht wäre es in der Tat nicht schlecht ein namesapce-statement in Python einzuführen, damit man die Möglichkeit hat zusammengehörige Funktionen optisch zu gruppieren.
Machbar ist es schon jetzt. Man könnte entweder über die Codecs gehen und dort den Quellcode vorher abfangen und in anderen Konstrukte übersetzen oder versuchen den Interpreter so zu hacken, dass er das unterstützt (wobei ich da nicht sicher bin, was geht und was nicht).

Andererseits verstehe ich immer noch nicht, warum man nicht einfach ein zusätzliches Modul nimmt. Die Diskussion habe ich heute schon mal geführt, bei der es darum ging zusätzliche Dateien zu erstellen, statt bereits vorhandene zu verkomplizieren.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
poker
User
Beiträge: 146
Registriert: Donnerstag 20. September 2007, 21:44

Das ist da doch zuviel gehacke. Dann kann man auch gleich ``class`` als namespace Nutzen. Aber dan kommen die jenigen mit ihrer "reinen" Lehre.

@Zusätzliches Modul: Ich denke gerold geht es darum nur ein Script weiter zu geben anstelle von einem ganzes package. Kann ich gut verstehen. BeautifulSoup ist denk ich gerade mit deshalb so beliebt. Einfach zu integrieren :)
Antworten