Seite 1 von 1
Funktionen innerhalb __init__ - Methode..?
Verfasst: Sonntag 13. Februar 2011, 12:26
von Don Polettone
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
Re: Funktionen innerhalb __init__ - Methode..?
Verfasst: Sonntag 13. Februar 2011, 13:14
von 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.
Re: Funktionen innerhalb __init__ - Methode..?
Verfasst: Sonntag 13. Februar 2011, 14:55
von Don Polettone
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

Ich versuche das mal.
...und auch das mit dem Dictionary werde ich versuchen, umzusetzen. Danke!
Grüsse,
Henry
Re: Funktionen innerhalb __init__ - Methode..?
Verfasst: Sonntag 13. Februar 2011, 15:54
von 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.
Re: Funktionen innerhalb __init__ - Methode..?
Verfasst: Sonntag 13. Februar 2011, 16:45
von yipyip
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
yipyip
Re: Funktionen innerhalb __init__ - Methode..?
Verfasst: Montag 14. Februar 2011, 10:13
von Don Polettone
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
Re: Funktionen innerhalb __init__ - Methode..?
Verfasst: Montag 14. Februar 2011, 10:26
von Xynon1
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
Re: Funktionen innerhalb __init__ - Methode..?
Verfasst: Montag 14. Februar 2011, 10:32
von 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.
Re: Funktionen innerhalb __init__ - Methode..?
Verfasst: Montag 14. Februar 2011, 10:56
von Xynon1
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.
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

Re: Funktionen innerhalb __init__ - Methode..?
Verfasst: Montag 14. Februar 2011, 12:39
von yipyip
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.
yipyip
Creep Modul - einige Fragen
Verfasst: Donnerstag 17. Februar 2011, 19:52
von Don Polettone
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