design pattern : strategy mit python

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.
Antworten
snape
User
Beiträge: 5
Registriert: Freitag 14. Dezember 2007, 20:19

Hallo,

bisher habe ich (hobbymäßig) mit Java programmiert und möchte nun in Python (PyQt) reinschnuppern.

Bisher habe ich noch nicht die richtige "Python-Denke" gefunden ....

Zum Üben möchte ich ein "strategy"-projekt schreiben.

Es soll eine Klasse "Steppentier" geben, davon werden dann verschiedene Tiere abgeleitet, z.B. Antilope, Loewe, Geier ...

Ich habe eine "Interface" - Klasse "Fressgewohnheit".
Davon wiederrum sind die Klassen "FleischFresser", "GrasFresser" und "AasFresser" abgeleitet.

jetzt moechte ich in der Klasse "Steppentier" eine Referenzvariable auf die Interfaceklasse haben, diese aber erst in den von "steppentier" abgeleiteten klassen "Antilope", "Loewe", "Geier" initialisieren.

Wie kann ich das mit python machen?


vielen Dank für jeden Tipp!!

beste Gruesse


Falls meine Beschreibung wirr war, das Beispiel in Java (ungetested) :

public interface Fressgewohnheit(){ public void fressen(); }

public class FleischFresser implements Fressgewohnheit {
public void fressen(){System.out.println("Fleisch");}}

public class GrasFresser implements Fressgewohnheit {
public void fressen(){System.out.println("Gras");}}

public class AasFresser implements Fressgewohnheit {
public void fressen(){System.out.println("Aas");}}

public abstract class Steppentier(){
Fressgewohnheit fg;
public Steppentier(){ /*foo*/}
public void MahlZeit(){fg.fressen();}
}

public class Antilope extends Steppentier{
public Antilope(){ fg = new GrasFresser();}
}

public class Loewe extends Steppentier{
public Loewe(){ fg = new FleischFresser();}
}

public class Geier extends Steppentier{
public Geier(){ fg = new AasFresser();}
}


public class RunIt{
public static void main(String[] args){
Steppentier otto = new Antilope();
Steppentier willi = new Loewe();
Steppentier fritz = new Geier();
otto.MahlZeit();
willi.MahlZeit();
fritz.MahlZeit();
}
}
BlackJack

OMG! Vergiss den ganzen Quatsch und implementiere einfach die Tiere. Basisklassen nur für gemeinsam verwendeten Code.

Bzw. lass das Projekt ganz bleiben wenn das wirklich so sinnlos ist wie's aussieht.

Code: Alles auswählen

class Steppentier(object):
    def fressen(self):
        print self.nahrung


class Loewe(Steppentier):
    nahrung = 'Fleisch'

class Antilope(Steppentier):
    nahrung = 'Gras'

class Geier(Steppentier):
    nahrung = 'Ass'
Mad-Marty
User
Beiträge: 317
Registriert: Mittwoch 18. Januar 2006, 19:46

Blackjack hat's schon korrekt beantwortet, wenn du aus irgendeinen Grund die Fress-objects halt noch brauchst erstell eben noch die Nahrungsklassen, aber ansonsten vergiß die interfaces.
snape
User
Beiträge: 5
Registriert: Freitag 14. Dezember 2007, 20:19

Hallo,

in so einem einfachen Beispiel ist es tatsaechlich einfacher, einfach zu vererben.
Aber, wie gesagt, ich moechte gerne verstehen, wie man in python dieses Prinzip (auf Schnittstelle programmieren, nicht auf Implementierung)(Komposition statt Vererbung)
umsetzt.



Ich habe etwas ueber "duck typing" gefunden, vielleicht ist das das was ich suche.
(http://www.aleax.it/gdd_pydp.pdf)

Vielen Dank fuer jeden Tipp!
Beste Gruesse!
BlackJack

Den Unterschied Schnittstelle und Implementierung gibt es in Python so nicht. Das ist bei Java ja nur sinnvoll damit man die Implementierung austauschen kann. Dazu muss man gegen eine formale Schnittstelle programmieren. In Python programmiert man direkt gegen die Implementierung, die lässt sich dank dynamischer Typisierung aber trotzdem austauschen.

Es geht in Python nicht hauptsächlich um die Typen sondern um das Verhalten der Objekte. Eben "duck typing". Wenn es eine Methode `fressen()` hat, kann man es als `Steppentier` verwenden, auch wenn es nicht davon erbt. Um das Beispiel von oben durch eine `WuestenEnte` zu erweitern die kein `Steppentier` ist, aber irgendwie doch ein Steppentier ist:

Code: Alles auswählen

class Steppentier(object):
    def fressen(self):
        print self.nahrung


class Loewe(Steppentier):
    nahrung = 'Fleisch'

class Antilope(Steppentier):
    nahrung = 'Gras'

class Geier(Steppentier):
    nahrung = 'Aas'


class WuestenEnte(object):
    def fressen(self):
        print 'SPAM'

for tier_class in (Loewe, Antilope, Geier, WuestenEnte):
    tier = tier_class()
    tier.fressen()
snape
User
Beiträge: 5
Registriert: Freitag 14. Dezember 2007, 20:19

cool :-)

vielen dank
und
beste gruesse
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

snape hat geschrieben:Aber, wie gesagt, ich moechte gerne verstehen, wie man in python dieses Prinzip (auf Schnittstelle programmieren, nicht auf Implementierung)(Komposition statt Vererbung)
umsetzt.
Durch Konventionen, nicht durch Syntax.

Es gibt einige Ansätze, sich interfaces selbst zu bauen, die dann zur Laufzeit Exceptions werfen, wenn man vergessen hat, eine Methode des Interface in seiner Klasse nachzubauen, aber aus meiner (immer noch kurzen) Erfahrung mit Python ist es einfach eine Frage der Dokumentation.

Bedenke, du hast Mehrfachvererbung in Python und keine Einschränkungen durch ein statisches Typsystem. Das erspart die meisten Klimmzüge, die man bei Java machen muss.

Stefan
snape
User
Beiträge: 5
Registriert: Freitag 14. Dezember 2007, 20:19

wenn ich das richtig verstanden habe, koennte der code dann so aussehen ...

Code: Alles auswählen

class Steppentier(object):
    def __init__(self):
        print "Steppentier"
class Fleischfresser(object):
    def fressen(self):
        print "Ich fresse %s" % self.nahrung
class Grasfresser(object):
    def fressen(self):
        print "%s finde ich lecker" % self.nahrung
#-------------------------------------------------------------------
class Loewe(Steppentier, Fleischfresser):
    def __init__(self):
        print " ### Ich bin ein Löwe"
    nahrung = 'Fleisch'
class Antilope(Steppentier, Grasfresser):
    def __init__(self):
        print " ### Antilope nennt man mich"
    nahrung = 'Gras'
class TotesTier(Steppentier):
    def __init__(self):
        print " ### Ich bin ein totes Tier."
        #super muss nicht an erster stelle stehen?? !!
        super(TotesTier, self).__init__()
#-------------------------------------------------------------------
for tier_class in (Loewe, Antilope, TotesTier):
    tier = tier_class()
    if hasattr(tier, "fressen") and callable(tier.fressen):
        tier.fressen()
    else:
        print "%s hat keine fressen-Methode" % type(tier)
vielen dank fuer Kommentare und tipps.
BlackJack

Der Code könnte so aussehen, aber ich persönlich versuche Mehrfachvererbung zu vermeiden. Das macht die Sache nur unnötig komplex und man ist unter Umständen mit `super()` konfrontiert, was ich noch mehr vermeide. Lesestoff zu `super()`: http://fuhm.org/super-harmful/

Wer das danach noch benutzen möchte, verdient die Probleme die man damit bekommen kann. ;-)

Die Tests vor dem Aufruf sind "unpythonisch". Ob es das Attribut gibt und man es aufrufen kann, findet man auch heraus indem man es einfach tut. Dazu im Glossar der Python Dokumentation EAFP (Easier to ask for forgiveness than permission) und LBYL (Look before you leap) nachlesen.

Was mich an dem Beispiel und der Fragestellung so ein bisschen stört ist, dass hier eine "Lösung" in Form eines Entwurfsmusters vorliegt und nach einem Problem gesucht wird. Was IMHO zu javatypischen "overengineered" APIs führt.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

BlackJack hat geschrieben:Der Code könnte so aussehen, aber ich persönlich versuche Mehrfachvererbung zu vermeiden. Das macht die Sache nur unnötig komplex und man ist unter Umständen mit `super()` konfrontiert, was ich noch mehr vermeide. Lesestoff zu `super()`: http://fuhm.org/super-harmful/
Hm, der Artikel klagt, dass man bei Python (im Gegensatz zu Dylan) viel falsch machen kann. Mit großer Macht kommt große Verantwortung. Ich würde eher sagen, der Name `super` ist unglücklich gewählt, auch in Python hätte man das `next_method` nennen sollen. Dann wäre es für mich klarer gewesen. Allerdings kannte ich Dylan und CLOS bereits vor Python und deren method resolution order war mir vertraut.

Ich finde es jedoch schwer, einerseits von Mehrfachvererbung abzuraten und gleichzeitig zu erklären, dass man Interfaces nicht braucht. IMHO ist das eine oder andere notwendig, wenn man mehr als nur Konventionen benutzen möchte, um "is a"-Typen zu definieren.

Ich stimme zwar prinzipiell zu, dass Klassen eher ein Implementierungshilfsmittel sein sollten als ein Vehikel zum Implementieren von Typhierarchien, doch 90% alle Programmierer sehen das anscheinend anders. Aber dann müssen sie auch damit leben, dass es manchmal kompliziert werden kann ;)

Stefan
BlackJack

Aber das sollen die doch dann bitte in C#, C++, Java & Co machen und nicht in Python. ;-)
Antworten