Funktionen innerhalb __init__ - Methode..?

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
Benutzeravatar
Don Polettone
User
Beiträge: 115
Registriert: Dienstag 23. November 2010, 20:26
Wohnort: Schweiz

Hallo mal wieder,

ich habe da ein Problem in Sachen Kontrollfluss wie mir scheint. Möchte mir da eine Klasse basteln, welche Creeps initialisiert für mein Tower Defense - Spiel. Nun bastle ich gerade an der initialize Methode. Die Sache ist nun, dass ich innerhalb der __init__ - Methode weitere Funktionen abrufen muss, und diese muss ich ausserhalb der __init__ - Methode definieren, sonst funzt irgendwie nicht... und genau das ist mein Problem. Hier der Code:

Code: Alles auswählen

# -*- coding: cp1252 -*-

# Creeps - Klasse

class Creeps():
    "Erzeugt Creeps"

    creeps = [] # jedes erzeugte Creep landet in dieser Liste und wird gelöscht,
                     # wenn es stirbt oder das Ziel erreicht
    
    def __init__(self, typ, level): # (initialize-Methode)
        self.typ = typ
        self.level = level
        print "Creep erzeugt. \nTyp:", typ, "\nLevel:", level
        
        self.attr = attribute_laden(typ, level)  # die Statuswerte des Creep werden geladen und in einem Dictionary gespeichert
        print "Attribute geladen. Attribute sind:", self.attr

        Creeps.creeps.append(self) # jetzt wird das erzeugte Creep der creeps-Liste hinzugefügt
        print "Creep in Creeps.creeps gespeichert:", Creeps.creeps 
    
def attribute_laden(typ, level):
    "Lädt die Statuswerte des erstellten Creep"
    
    if typ == "blob":
        attribute = {"walkcycle_x" : 5,
        "walkcycle_y" : 5,
        "cycle_trigger" : 12,
        "speed" : 4}
    elif typ == "zombie":
        pass

    return attribute


blob = Creeps("blob", 1)
Erst mal soll der Blob, welchen ich erstelle, seine Attribute laden und zuweisen. Die Funktion dafür muss ich aber ausserhalb der __init__-Methode definieren, und das passt mir nicht... sorry, ich bin noch immer Anfänger und habe gelesen, geändert und probiert, aber ich komme hier irgendwie nicht weiter. Hat jemand einen Rat am Start?

Danke und viele Grüsse,


Henry
Zuletzt geändert von Anonymous am Sonntag 13. Februar 2011, 12:59, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
Ich code, also bin ich.
BlackJack

@Henry Jones Jr.: Du kannst die Funktion auch innerhalb der `__init__` definieren. Muss halt vor dem Aufruf sein, aber ansonsten ist das kein Problem. Obwohl ich das vom Entwurf her nicht machen würde, weil irgendwie unschön, weil man dann viel zu viel in diese Initialisierung packt bei dem es Sinn machen würde es zu trennen.

Der Entwurf ist aber sowieso komisch. Die Klasse heisst `Creeps`, was erst einmal auf eine Containerklasse für Creeps hinweist, aber es wird bei einem Aufruf nur *ein* Creep erstellt. Aber gleichzeitig die erstellten Creeps in eine "globale" Liste auf der Klasse gespeichert!?

Und statt das die `__init__()` die Daten erhält um einen `Creep` zu erstellen, bekommst sie einen "Typ" über den dann die Daten aus einer dort fest "verdrahteten" Funktion ermittelt. Und diese Funktion willst Du dann auch noch in die `__init__()` *hinein ziehen*. Statt das man eine Funktion hat, die über einen Typ die Daten für einen `Creep` ermittelt und den dann damit erstellt. Und sich somit die Möglichkeit offen hält `Creep`\s auch auf andere Weisen zu erstellen.

Kommentare sollten einen Mehrwehrt bringen. Vor ``class Creeps`` zu Kommentieren, dass das die Creeps-Klasse ist, oder hinter der `__init__()` nochmal anzumerken, dass es sich um die "initialize-Methode" handelt, ist total überflüssig. Genau wie der Kommentar hinter ``Creeps.creeps.append(self)``. Genau dass steht da schon sehr offensichtlich als Code! Quelltext sollte so geschrieben werden, dass man nicht dran schreiben muss *was* er tut. Kommentare sollten beschreiben *warum* der Code etwas tut -- wenn das aus dem Code selber nicht offensichtlich wird.

In `attribute_laden()` könnte man die ``if``/``elif``-Abfrage durch ein Dictionary ersetzen.
Benutzeravatar
Don Polettone
User
Beiträge: 115
Registriert: Dienstag 23. November 2010, 20:26
Wohnort: Schweiz

Hi BlackJack,

danke für die vielen Hinweise! Also bzgl. der Kommentare: Ist klar. Die waren eigentlich nicht für mich gedacht...

Bzgl. der creeps-Klasse: Ich hatte mir das so vorgestellt, dass ich im Mainscript das creeps Modul (also die Klasse) importiere und dann der jeweilige Level die creeps Klasse dazu bringt, dass sie die jeweiligen creeps produziert. Jedes Creep soll dann gleich all seine Instanzvariablen initialisieren (auch dessen Bilder, Statuswerte etc.) und jedes creep wird dann der Klassenvariable creeps hinzugefügt. Dann könnte ich in der Hauptschleife z.Bsp. schreiben:

for creep in Creeps.creeps:
creep.move
... etc etc

dann ganz am Schluss das ganze Zeugs wieder auf den Bildschirm blitten und flippen.

Dann wäre es also klüger, im Spielscript selbst eine Funktion zu definieren wie z.Bsp. creeps_erzeugen oder so, welche der creep-Klasse die Werte der zu erzeugenden Creeps übergibt und dann die erzeugten Creeps zurückgibt? Klingt ganz hübsch :D Ich versuche das mal.

...und auch das mit dem Dictionary werde ich versuchen, umzusetzen. Danke!

Grüsse,


Henry
Ich code, also bin ich.
BlackJack

@Henry Jones Jr.: Falls das mit dem "blitten" ein Hinweis auf Pygame war -- da würde ich für "Gruppen von beweglichen Bildern" mal einen Blick auf die Sprites- und Gruppen-Klassen und -Funktionen werfen. Damit kann man ganze Gruppen verwalten, blitten lassen, auf Kollisionen prüfen usw.
yipyip
User
Beiträge: 418
Registriert: Samstag 12. Juli 2008, 01:18

Hier mal eine etwas andere Strukturierung (mehr oder weniger auf die Schnelle). Vielleicht entspricht sie ja grob Deinen Vorstellungen:

Code: Alles auswählen

class Creep(object):

   
     def __init__(self, level):
      
        self.level = level


     def __str__(self):

        return str(self.__dict__)

        

class Blob(Creep):


     def __init__(self, level, walkcycle_x=5, cycle_trigger=12, speed=4):

        Creep.__init__(self, level)
        self.walkcycle_x = walkcycle_x
        self.cycle_trigger= cycle_trigger
        self.speed = speed



class Zombie(Creep):


     def __init__(self, level, strength=10):

         Creep.__init__(self, level)
         self.strength = strength



class CreepFactory(object):


     def __init__(self, *type_level_dicts):

        self.creeps = [{'blob': Blob, 'zombie': Zombie}[ty](lev, **dict)
                       for (ty, lev, dict) in type_level_dicts] 
      
    
     def __iter__(self):

        return iter(self.creeps)


      
if __name__ == '__main__':

     c1 = 'blob', 1, {'speed':22, 'cycle_trigger':100}
     c2 = 'zombie', 2, {'strength':33}

     fact =  CreepFactory(c1, c2)
     for creep in fact:
         print creep
:wink:
yipyip
Benutzeravatar
Don Polettone
User
Beiträge: 115
Registriert: Dienstag 23. November 2010, 20:26
Wohnort: Schweiz

Heipähei yipyip :-)

vielen Dank für den T(r)ip bzgl. Struktur, sieht echt knackig aus! Ich muss mich unbedingt mal ins Klassenzeugs einlesen (Superklassen und Subklassen etc). 2 Sachen aber verstehe ich gar nicht; kannst Du mir die kurz erklären oder mir einen Link posten wo ich mich schlau machen kann..?

Das verstehe ich nicht:

Code: Alles auswählen

     def __init__(self, *type_level_dicts):

        self.creeps = [{'blob': Blob, 'zombie': Zombie}[ty](lev, **dict)
was tun die Asterisk da genau..? Kenne die bisher nur von "from nih.py import *"... und dann noch 2 Sterne..? Was tut das..?

und dann noch das "[ty]"... was ist das? Das wird ja nirgends definiert oder schon? Auch die doppelt "underlineten" Funktionen (abgesehen von __init__ natürlich) sagen mir nicht viel... Ist nun ein bisschen viel, was ich nicht kenne, daber ich möchte das unbedingt verstehen...

Kann jemand helfen..?

Vielen Besten für die Hilfe soweit, weiss dies sehr zu schätzen!

es grüsst und dankt:


Henry
Ich code, also bin ich.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Die Asterisk, sollte man auch nicht zwangsläufig verwenden.
Diese Art sollte man bei generischen Argumenten nicht anwenden, es wäre hier in beiden Fälle besser eine Liste und eine normales Dictionary zu verwenden.

Das einzelne * bewirkt hier, das du eine unbegrenzte Anzahl an Parametern direkt beim initialisieren der Instanz über geben kannst. Die Alternative wäre hier direkt eine Liste zu übergeben, da am Ende das gleiche heraus kommen würde.

Das doppelte ** bewirkt hier, das "entpacken" einer Dictionary, zur Veranschaulichung, könnte man das so darstellen: Aus {"hallo":1, "welt":2} würde hallo=1, welt=2 werden.

Die Variable "ty" kommt aus der Iteration, genau wie "dict" und "lev" das Stichwort hierfür ist "List Comprehension". Hier ist noch anzumerken, das man Variablennamen, welche von Build-In Funktionen genutzt werden zu vermeiden sind.

Allgemein denke ich den Konstrucktor von "CreepFactory" könnte man um einiges schöner schreiben.

Edit: "__str__" wird aufgerufen wenn die Instanz in einen String umgewandelt wird und "__iter__" ermöglicht über die Instanz zu iterieren. Mehr findest du hier http://docs.python.org/reference/datamo ... tomization
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
BlackJack

@Xynon1: Ich würde sogar sagen `CreepFactory` insgesamt ist "javaesque"/unnötig kompliziert und "magisch". Das hätte man auch einfach als Funktion und ohne ``*``-Magie schreiben können, die eine Liste mit `Creep`\s zurüch liefert. Und die festverdrahtete "Zeichenkette auf `Creep`-Unterklasse"-Abbildung ist auch eine unnötige Indirektion.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Ja, hatte ich mir auch schon gedacht, im Grunde macht Blob und Zombie hier noch keinen Sinn, außer man hat natürlich sehr unterschiedliche Eigenschaften bei beiden. Ob nun Funktion oder Klasse, kommt natürlich nun auch auf den Umfang des ganzen an, in diesem Beispiel bräuchte man ja noch nicht mal wirklich Creep-Klasse. :roll:

Wenn es allerdings mehr Informationen werden, finde ich eine CreepFactory als Objekt keine schlechte Idee, zudem man davon eventuell mehrere baut. Ansonsten stimm ich dir ntürlich vorbehaltlos zu :D
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
yipyip
User
Beiträge: 418
Registriert: Samstag 12. Juli 2008, 01:18

Ich bin erstmal davon ausgegangen, daß Blob und Zombie sehr unterschiedlich sind. Stimmt, Creepfactory hat etwas javaesqes an sich, habe es hauptsächlich deshalb so gemacht, um einen Bezug auf die Typunterscheidung in "attribute_laden" herzustellen. Für die nachfolgenden Versionen sollte man die von BlackJack angesprochene Indirektion mittels Zeichenketten auch eher vermeiden.

:wink:
yipyip
Benutzeravatar
Don Polettone
User
Beiträge: 115
Registriert: Dienstag 23. November 2010, 20:26
Wohnort: Schweiz

Hallo da nochmals,

habe da nun noch ein wenig gebastelt und so kommt das schon besser, finde ich. Ich möchte ja ein TD-Game basteln, dazu brauche ich meine Creep-Klasse. Nun hab' ich mir gedacht, am liebsten hätte ich ein Creep-Modul. Dann habe ich das Hauptscript des Spiels, welches das Creep-Modul importiert und dieses benutzt (wohl via dort gecodeter Klasse oder Funktion), um die Waves zu erstellen. Im Moment sieht dieses Creep-Modul SO aus (Work in Progress; es geht mir nicht um die Syntax, nur um die Struktur):

Code: Alles auswählen

import grafiken

def get_creep(typ):
    """Gibt die Attribute des Creeps name zurück"""
    creeps = {}

    creeps["blob"] = {"wcycle_x" : 5, "wcycle_y" : 5, "wcycle_trigger" : 13, "speed" : 4, "hp" : 100}

    return creeps[typ]

def get_imageset(typ, cycle_x, cycle_y):
    """"gibt das Imageset des Creeps name zurück"""

    pfad = "Creeps"

    abc = ("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m")
    x_cycles = abc[:cycle_x]
    y_cycles = abc[:cycle_y]
    
    directions = ("2", "8", "6", "4")
    imageset = {}

    for direction in directions:
        bilder = []
        
        if direction == "2" or direction == "8":
            cycles = y_cycles
        elif direction == "6":
            cycles = x_cycles
            
        if richtung == "2" or richtung == "8" or richtung == "6"
            for n in cycles:
                bildname = typ + "_" + direction + n + ".png"
                bild = grafiken.bild_laden(pfad, bildname, True)
                bilder.append(bild)
            imageset[direction] = bilder
            
        elif direction == "4":
            for n in imageset["6"]:
                bild = grafiken.bild_spiegeln(n, False, True)
                bilder.append(bild)
            imageset[direction] = bilder

    return imageset

class Creep():
    """Erzeugt ein Creep"""

    def __init__(self, typ):
        self.typ = typ
        self.attr = get_creep(typ)
        self.imageset = get_imageset(typ, self.attr["wcycle_x"], self.attr["wcycle_y"])

    #def einreihen(self, karte):
Natürlich bekommen meine Creeps dann noch viel mehr Instanzvariablen... Wäre es evtl. sinnvoll, die Attributwerte wie HP und Speed etc. von Systemwerten wie der Anzahl Bilder im X-Achsen-Walkcycle ( self.attr["wcycle_x"] ), zu trennen..?

Und noch was stresst mich: Es geht um IDLE. Es kam schon oft vor, dass, wenn ich eine eigens gecodete Funktion aufrief, ein "Pop-Up" aufging in IDLE und dieses mir die Funktion zeigte; inkl. __doc__, direkt während des Codens. Aber das tut er einfach nicht mehr... habe ich da was ausgeschalten oder wieso macht der das nicht mehr? Jemand eine Idee..?

Vielen Dank und viel Spass beim Schlangenzähmen,


Henry
Ich code, also bin ich.
Antworten