Klasse verschiedene varianten

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
snafu
User
Beiträge: 6854
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Und im Hinblick auf den "Register-Quatsch" hatte ich ja schon im Code angedeutet, dass man die Registierung der Gebäudetypen in __init__ als Dictionary Comprehension per Einzeiler abfrühstücken kann. Eine nachträgliche Registrierung von Gebäudetypen während des Spiels ist doch sowieso unrealistisch. Oder geht es um die Freischaltung weiterer Gebäudetypen? Dann würde ich das wahrscheinlich eher über ein activated-Atrribut lösen. Denn ich kenne es so, dass Gebäude aus höheren Stufen zunächst ausgegraut sind. Dass ein angefangenes Spiel plötzlich komplett neue Gebäudetypen erhält, habe ich bisher noch nicht gesehen. Wenn das irgendwann möglich sein soll, dann wäre eine Plugin-API für diesen Punkt vielleicht geeigneter.
Benutzeravatar
snafu
User
Beiträge: 6854
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

kbr hat geschrieben:
snafu hat geschrieben:Also wie schon vorgeschlagen wurde, ruhig zu Beginn ein paar Containerklassen definieren, jeweils für Koordinaten, Feldinfo und sowas.
Noch eine Stufe vorher empfehle ich ein großes Blatt Papier, einen Bleistift, sowie einen *großen* Radiergummi. :)
Wobei man IMHO schon viel Erfahrung braucht, um so ein Projekt quasi auf dem Papier durchplanen zu können. Der OP kommt mir so vor als wenn er zwar nicht gänzlich neu auf dem Feld der Programmierung ist, aber noch recht am Anfang bei der Progammierung komplexer Spiele steht. Vieles ist dann auch einfach Learning By Doing mit dem häufigen auf die Nase fallen inklusive.
Benutzeravatar
kbr
User
Beiträge: 1506
Registriert: Mittwoch 15. Oktober 2008, 09:27

snafu hat geschrieben:Wobei man IMHO schon viel Erfahrung braucht, um so ein Projekt quasi auf dem Papier durchplanen zu können.
Etwas im Vorfeld fehlerfrei durchplanen zu können halte ich für unrealistisch. Es geht vielmehr darum einen Einstieg zu finden, der einem später möglichst wenig Kopfschmerzen bereitet. Ohne Plan zu starten ist schlecht - selbst bei agiler Entwicklung :)
Pygoscelis papua
User
Beiträge: 206
Registriert: Freitag 13. März 2015, 18:36

Also das Blatt Papier existiert schon :D
Ich habe: - eine Liste Für die meilensteine,
- ein Klassendiagramm
- ein Groben schriftlichen Text für die Anforderungen
- einen Plan für die Graphische Oberfläche

Und die Änderungen von Snafu sind auch schon übernommen,
die Registrierung ist jetzt eben nicht mehr in der Game-Klasse sondern in dem building_types.py Modul, von welchem man das importieren kann.
Den Rest schaue ich mir heute mal an.
Vieleicht habe ich in letzter Zeit zu viel lua und pascal programmiert und muss mich in Python erst wieder reinfinden :)
import this
hidden python features

JAVA = Just Another Vulnerability Announcement :D
Pygoscelis papua
User
Beiträge: 206
Registriert: Freitag 13. März 2015, 18:36

Ich bin gerade noch dabei die Anmerkungen welche ihr mir gegeben habt um zu setzten.
Dabei ist aber etwas sehr seltsames passiert. Ich habe die klasse Planet wie folgt definiert:

Code: Alles auswählen

class Planet():
    def __init__(self, position, size):
        self.position = position
        self.size = size
        self.map = [
            [MapField((x, y), ground_types["G"]) for x in range(size)] 
            for y in range(size)
        ]
        print(type(self))
 
    def __repr__(self):
        class_name = type(self).__name__
        x, y = self.position
        return '{}(x={}, y={})'.format(class_name, x, y)
 
    def place_commando_center(self, owner):
        position = _randpos(self.size)
        return self.build_building(position, "Commando Center", owner)
 
    def has_commando_center(self, player): #must be tested!
        return any(
            field.get_owner() == player and 
            isinstance(field.get_building(), CommandoCenter)
            for field in self._get_all_fields()
        )
        
    def get_field(self, position):
        x, y = position
        return self.map[x][y]
   
    def set_ground(self, position, ground):
        field = self.get_field(position)
        field.set_ground(ground)

    def build_building(self, position, building_name, owner):
        field = self.get_field(position)
        return field.build_building(self, building_name, owner)

    def _show(self):
        print(self)
        print('\n'.join([str(i) for i in self.map]))

    def _get_all_fields(self):
        return [field for row in self.map for field in row]
und jetzt wird mir immer der __name__ von allen Planet-instanzen auf "instance" gesetzt, wobei die Methoden gleich verwendet werden können.
Das bedeutet die ausgabe von planetinstance.__repr__() ist dann "instance{x=..., y=...}".
wie kann das passiert sein?
Zuletzt geändert von Anonymous am Samstag 14. Januar 2017, 18:41, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
import this
hidden python features

JAVA = Just Another Vulnerability Announcement :D
Benutzeravatar
Kebap
User
Beiträge: 776
Registriert: Dienstag 15. November 2011, 14:20
Wohnort: Dortmund

Pygoscelis papua hat geschrieben:

Code: Alles auswählen

class Planet():
    def __repr__(self):
        class_name = type(self).__name__
        x, y = self.position
        return '{}(x={}, y={})'.format(class_name, x, y)
jetzt wird mir immer der __name__ von allen Planet-instanzen auf "instance" gesetzt
die ausgabe von planetinstance.__repr__() ist dann "instance{x=..., y=...}".
wie kann das passiert sein?
__repr__ hast du ja selbst definiert und vorgegeben, was da bei return zurückkommt. Neben x und y auch class_name. Praktischerweise wird class_name genau 2 Zeilen drüber festgelegt. Wenn du dir das anschaust, welche Ausgabe würdest du erwarten, wenn nicht "instance"? Solche Fragen kann man gut im interaktiven Interpreter ausprobieren und Schritt für Schritt leichter oder komplizierter werden, bis alle Unklarheiten beseitigt sind. :wink:
MorgenGrauen: 1 Welt, 8 Rassen, 13 Gilden, >250 Abenteuer, >5000 Waffen & Rüstungen,
>7000 NPC, >16000 Räume, >200 freiwillige Programmierer, nur Text, viel Spaß, seit 1992.
BlackJack

@Kebap: Also mich würde auch interessieren warum da 'instance' bei raus kommen soll:
[codebox=pycon file=Unbenannt.txt]>>> class Planet():
... def __repr__(self):
... class_name = type(self).__name__
... x, y = self.position
... return '{}(x={}, y={})'.format(class_name, x, y)
...
>>> p = Planet()
>>> p
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __repr__
AttributeError: 'Planet' object has no attribute 'position'
>>> p.position = (1,2)
>>> p
Planet(x=1, y=2)
>>> type(p)
<class '__main__.Planet'>
>>> type(p).__name__
'Planet'[/code]

@Pygoscelis papua: Konvention für `__repr__()` ist das da entweder etwas raus kommt das man im Quelltext so schreiben könnte um ein gleiches Objekt zu erhalten, oder das die `repr()`-Darstellung in spitze Klammern eingefasst wird. Beispielsweise '<Planet x=… y=…>'.
Benutzeravatar
snafu
User
Beiträge: 6854
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Der Sinn, warum man den Namen dynamisch über das __name__ Attribut des Typen ermittelt anstatt ihn einfach direkt hinzuschreiben, ist übrigens der Gedanke an Vererbung. Wenn jetzt jemand einen RedPlanet ableiten würde, dann zeigt __repr__() einem automatisch RedPlanet(...) an, ohne dass man die Methode anpassen müsste. Ausgenommen sind natürlich Fälle, wo neue Attribute eingeführt werden. Die werden dann nicht automatisch angezeigt. Wenn man das auf die Spitze treiben möchte, dann ginge auch so etwas:

Code: Alles auswählen

class Planet(object):
    def __init__(self, position, size):
        self.position = position
        self.size = size
        self._repr_names = ['position', 'size']

    def __repr__(self):
        class_name = type(self).__name__
        signature = ', '.join(
            '{}={!r}'.format(name, getattr(self, name))
            for name in self._repr_names
        )
        return '{}({})'.format(class_name, signature)


class ColoredPlanet(Planet):
    def __init__(self, position, size, color):
        Planet.__init__(self, position, size)
        self.color = color
        self._repr_names.append('color')


def main():
    planet = Planet((23, 42), 2342)
    red_planet = ColoredPlanet((23, 42), 2342, 'red')
    print(planet)
    print(red_planet)

if __name__ == '__main__':
    main()
Aber das nur nebenbei. Gibt ja bereits nametuple(), sodass man diese Dinge eigentlich nicht nochmal neu erfinden muss. In echtem Code würde ich das wahrscheinlich als Mixin implementieren, wenn nametuple() mir aus irgendwelchen Gründen nicht geeignet erscheint.
Pygoscelis papua
User
Beiträge: 206
Registriert: Freitag 13. März 2015, 18:36

die Ausgabe instance ist aber nicht nur bei __repr__() sondern auch bei
type(Planet).__name__ bzw. wenn ich schreibe Planer(parameter).name
Woran kann das denn liegen? Ich kapier es einfach nicht.
Und die __repr__() funktion hat snafu geschrieben nicht ich :)
@kepab ich hätte dort die ausgabe "Planet" erwartet wie bei allen anderen klassen
meine klasse heißt ja nicht instance.
hier mal dazu eine Ausgabe aus meinem Interpreter:

Code: Alles auswählen

[GCC 4.9.3] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class House():
...     pass
... 
>>> House.__name__
'House'
>>> 
import this
hidden python features

JAVA = Just Another Vulnerability Announcement :D
Benutzeravatar
snafu
User
Beiträge: 6854
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Da stand nicht type(Planet).__name__, sondern type(self).__name__

Und niemand zwingt dich, Code zu übernehmen, dessen Sinn dir nicht klar ist. Dann kann man auch fragen oder es weglassen.
Benutzeravatar
snafu
User
Beiträge: 6854
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Übrigens, type(Planet()).__name__ ergibt "instance", wenn man die Klasse unter Python 2.x ohne Basisklasse definiert. Daher sollte man immer von object erben, wenn man keine andere Basisklasse hat. Sonst erhält man unter Python 2 oldstyle-Klassen.
Pygoscelis papua
User
Beiträge: 206
Registriert: Freitag 13. März 2015, 18:36

was ist denn da der Unterschied?
hier eine andere Ausgabe aus meinem interpreter:

Code: Alles auswählen

>>> class House:
...     def __repr__(self):
...         return type(self).__name__
... 
>>> a = House()
>>> a
House
>>> a.__repr__()
'House'
und ich verwende ja nur Python3
EDIT: ich habe gerade gesehen das meine IDE irgendwie auf Python2 umgestellt hatte, daher rührte der Fehler also :)
Da hätte ich jetzt auch noch wochen im code suchen können :D nächstes mal teste ich es auch außerhalb der IDE ...
import this
hidden python features

JAVA = Just Another Vulnerability Announcement :D
Benutzeravatar
snafu
User
Beiträge: 6854
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ich kenne deine Entwicklungsumgebung nicht, aber "instance" kann bei type(self).__name__ AFAIK nur unter Python 2.x herauskommen, wenn wie gesagt eine "nackte" Klasse ohne Basisklasse definiert wird. Python 3.x verwendet immer automatisch Newstyle-Klassen, auch ohne "object". Ich schreibe es trotzdem immer hin, weil es nicht wehtut und den Code damit kompatibel zu Python 2.x hält.

EDIT:
Dann hat sich das ja aufgeklärt. Und sorry, dass ich dich gerade so angefahren habe. Ich hatte das "ist nicht von mir -- snafu hat das so gezeigt" wohl ich den falschen Hals gekriegt... ;)
Zuletzt geändert von snafu am Samstag 14. Januar 2017, 22:26, insgesamt 1-mal geändert.
Pygoscelis papua
User
Beiträge: 206
Registriert: Freitag 13. März 2015, 18:36

ok dann mache ich das auch.
import this
hidden python features

JAVA = Just Another Vulnerability Announcement :D
Antworten