Beginner needs Help - Wie kann ich mein Programm verbessern?

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
Benutzeravatar
weitnow
User
Beiträge: 17
Registriert: Dienstag 8. September 2015, 15:36

Hallo liebe Leute

Ich heisse Christian und habe vor kurzer Zeit, als Hobby, angefangen, Python im Selbststudium zu lernen.

Das schwierigste für absolute Beginner wie mich ist ganz klar das objektorientierte Design. Testweise habe ich mir ein kleines Programm zusammengebastelt.

Kurze Erklärung:

Mit rambo = Soldier('Rambo') wird ein Objekt der Klasse Soldier mit dem Namen Rambo erzeugt.

rambo.addWeapon() fügt dann dem Objekt eine Objekt der Klasse Weapon hinzu
rambo.info() zeigt Informationen über den Soldaten Rambo
rambo.shoot(rambo) lässt rambo auf sich selbst schiessen.

Problem:

In der Klasse Weapon liegt in der Methode def __init__ noch ein Fehler. Bei self.shooter würde ich gerne anstelle des Objekts keusch einfach das Objekt(Typ Soldier) übergeben, welches
die Methode pulltriger aufruft, so dass die anschliessende Textausgabe korrekt ist.

Wenn sich jemand die Zeit nimmt, mein Programm kurz anzuschauen und mir zu sagen, ob ich damit überhaupt auf dem richtigen Weg bin oder ich es auch objektorientierter Sicht
anderst hätte schreiben sollen, dann wäre ich sehr dankbar.

Gruss

Christian

Code: Alles auswählen


class Soldier(object):
    
    def __init__(self, name):
        self.name = name
        self.health = 100
        self.weapon = []
        
    def info(self):
        f = "{:15}|{:<15}"
        seperation_line = (29 * "-")
        
        print (seperation_line)
        print ("SOLDAT")
        print (f.format("Name", self.name))
        print (f.format("Leben", self.health))
        print (seperation_line)
        print ("WAFFE")
        if self.weapon:
            print (f.format("Name", self.weapon[0].name))
            print (f.format("Munition", self.weapon[0].munition))
            print (f.format("Schaden", self.weapon[0].damage))
        else:
            print ("{} hat keine Waffe,\nmit Ausnahme seines Sackmessers").format(self.name)
            
    def shoot(self, target):
        self.target = target
        if self.weapon:
            self.weapon[0].pulltriger(self.target)
        else:
            print ("{} hat keine Waffe,\nmit Ausnahme seines Sackmessers").format(self.name)
        
        
    def addWeapon(self):
        self.weapon.append(Weapon())
        print ("{} ist nun mit einem {} mit {} Munition und {} Schaden ausgeruestet").format(self.name, self.weapon[0].name, self.weapon[0].munition, self.weapon[0].damage)
        
    def disarm(self):
        self.weapon = []
        print ("{} wurde entwaffnet und besitzt nur noch ein Sackmesser").format(self.name)
        
        
class Weapon(object):
    
    def __init__(self):
        self.name = 'Assault Rifle'
        self.munition = 20
        self.damage = 25
        self.shooter = keusch #hier liegt noch ein Fehler, ich setze hier manuell self.shooter = objekt keusch....wie kann ich objekt keusch durch den aufruf durch add weapon uebergeben?
        
    def pulltriger(self, target):
        target.health -= self.damage
        self.munition -= 1
        print ("{} feuert mit seiner {}").format(self.shooter.name, self.shooter.weapon[0].name)
        print ("{} wurde getroffen und hat noch {} Leben").format(target.name, target.health)
        
#Generate a Soldier named Rambo
rambo = Soldier('Rambo')


#Generate a Soldier named Keusch and add a Weapon
keusch = Soldier('Keusch')
keusch.addWeapon()

BlackJack

@weitnow: Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Wenn man das macht dann kann in `Weapon.__init__()` auch nicht mehr auf `keusch` zugegriffen werden weil der Name dann lokal in einer Funktion ist und nicht mehr einfach so im ganzen Modul verwendet werden kann.

Man muss den Träger der Waffe beim erstellen des Objekts als Argument übergeben wenn die Waffe ihren Träger kennen soll. Und eigentlich möchte man die anderen Attributwerte auch übergeben wenn man eine Waffe erstellt, statt feste Werte zu verwenden. Ich würde einer Waffe allerdings auch keinen festen Träger zuordnen sondern den beim abfeuern des Schusses übergeben.

Falls `munition` englisch sein soll bedeutet das wahrscheinlich nicht das was Du denkst. Und `damage` ginge zwar ist aber für meinen Geschmack zu mehrdeutig, denn man könnte auch denken das der Wert den Schaden beschreibt den die Waffe eingesteckt hat und nicht was sie pro Schuss austeilt.

Der Abzug heisst auf Englisch „trigger“ und ihn zu betätigen sind zwei Worte, also `pull_trigger()`.

Man sollte eine Waffe nur abfeuern können wenn sie überhaupt noch Munition hat.

Bei der Ausgabe in `pull_trigger()` greifst Du auf die erste Waffe des Schützen zu um den Namen der Waffe auszugeben. Das passt aber nicht wenn diese erste Waffe gar nicht die ist die abgefeuert wird. Man führt doch aber gerade eine Methode auf *der* Waffe aus die abgefeuert wird, also kann man da auch direkt auf deren Namen zugreifen ohne so komische Umwege.

`weapon` (Einzahl) ist ein unpassender Name für eine Liste mit Waffen (Mehrzahl).

In `Soldier.shoot()` wird das `target`-Attribut neu eingeführt. Davon abgesehen das man Attribute möglichst nur in der `__init__()` einführen sollte, macht das gar keinen Sinn diesen Wert zum Zustand des Objekts hinzuzufügen.

`add_weapon()` sollte man die neue Waffe als Argument übergeben, sonst kann man ja keine verschiedenen Waffen dort hinzuzufügen sondern nur was fest in dieser Methode kodiert ist. Ausserdem sollte man die Informationen für die letzte hinzugefügte Waffe ausgeben, sonst stimmt die Ausgabe nur für die erste Waffe.

Das mal für den Anfang umgesetzt könnte dann so aussehen:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function


class Soldier(object):

    def __init__(self, name):
        self.name = name
        self.health = 100
        self.weapons = list()

    def print_info(self):
        table_template = '{0:15}|{1:<15}'
        seperation_line = (29 * '-')

        print(seperation_line)
        print('SOLDAT')
        print(table_template.format('Name', self.name))
        print(table_template.format('Leben', self.health))
        print(seperation_line)
        print('WAFFE')
        if self.weapons:
            print(table_template.format('Name', self.weapons[0].name))
            print(table_template.format('Munition', self.weapons[0].ammunition))
            print(table_template.format('Schaden', self.weapons[0].hit_points))
        else:
            print(
                '{0.name} hat keine Waffe,\n'
                'mit Ausnahme seines Sackmessers'.format(self)
            )

    def shoot(self, target):
        if self.weapons:
            self.weapons[0].pull_trigger(self, target)
        else:
            print(
                '{0.name} hat keine Waffe,\n'
                'mit Ausnahme seines Sackmessers'.format(self)
            )

    def add_weapon(self, weapon):
        self.weapons.append(weapon)
        print(
            '{0.name} ist nun mit einem {1.name} mit {1.ammunition} Munition'
            ' und {1.hit_points} Schaden ausgeruestet'.format(
                self, self.weapons[-1]
            )
        )

    def disarm(self):
        self.weapons = list()
        print(
            '{0.name} wurde entwaffnet und besitzt nur noch ein Sackmesser'
                .format(self)
        )


class Weapon(object):

    def __init__(self, name, ammunition, hit_points):
        self.name = name
        self.ammunition = ammunition
        self.hit_points = hit_points

    def pull_trigger(self, shooter, target):
        if self.ammunition > 0:
            target.health -= self.hit_points
            self.ammunition -= 1
            print('{0.name} feuert mit seiner {1.name}'.format(shooter, self))
            print(
                '{0.name} wurde getroffen und hat noch {0.health} Leben'.format(
                    target
                )
            )


def main():
    rambo = Soldier('Rambo')

    keusch = Soldier('Keusch')
    keusch.add_weapon(Weapon('Assault Rifle', 20, 25))
    keusch.print_info()

    keusch.shoot(rambo)


if __name__ == '__main__':
    main()
Benutzeravatar
weitnow
User
Beiträge: 17
Registriert: Dienstag 8. September 2015, 15:36

@BlackJack:

Wow....einfach mega cool. Vielen lieben Dank, dass du dir die Zeit für die ausführliche Antwort inkl. kleine Englischkunde :wink: und Anpassung und Optimierung meines kleinen Beispielprogramms genommen hast. Das hilft mir wirklich weiter und ich weiss es sehr zu schätzen.

Ich werde jetzt einmal mit deinem optimierten Programm weitermachen und es als Übung weiter auszubauen versuchen.

Danke nochmals für deine wertvolle, schnelle Hilfe :D
Benutzeravatar
weitnow
User
Beiträge: 17
Registriert: Dienstag 8. September 2015, 15:36

Hallo zusammen

Blackjack war so lieb mein Programm zu verbessern, wobei ich viel lernen konnte und mir meine Holzhackermethoden vor Augen geführt wurden. Sprich
mein Programmcode war aufwendiger und unübersichtlicher als nötig.

Eine Frage hätte ich noch:
Ich nehme an, dass im verbesserten Code von Blackjack (siehe unten) die Zahl bei {0.name} und bei {1.name} jeweils für das erste und zweite Argument stehen, welche
dann bei Format angegeben werden. Aber wieso steht beim Code von Blackjack als Argument self.weapons[-1] und nicht self.weapons[0]?

Orignaler Code von mir:

Code: Alles auswählen

def addWeapon(self):
    self.weapon.append(Weapon())
    print ("{} ist nun mit einem {} mit {} Munition und {} Schaden ausgeruestet").format(self.name, self.weapon[0].name, self.weapon[0].munition, self.weapon[0].damage)
Verbesserter Code von Blackjack:

Code: Alles auswählen

def add_weapon(self, weapon):
    self.weapons.append(weapon)
    print('{0.name} ist nun mit einem {1.name} mit {1.ammunition} Munition und {1.hit_points} Schaden ausgeruestet'.format(self, self.weapons[-1]))
Zuletzt geändert von weitnow am Donnerstag 10. September 2015, 08:20, insgesamt 1-mal geändert.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Kurz: Dein Index ist falsch :)

Waffe 0 ist die erste Waffe, du willst allerdings Infos ueber die letzte ausgeben. -1 greift dagegen auf das letzte Listenelement zu, eben das das du gerade hinzugefuegt hast.

Alternativ kann man auch explizit das neue Element benennen und benutzen:

Code: Alles auswählen

def addWeapon(self):
    weapon = Weapon()
    self.weapon.append(weapon)
    print("....".format(self, weapon)
Benutzeravatar
weitnow
User
Beiträge: 17
Registriert: Dienstag 8. September 2015, 15:36

Hallo Cofi

Das ging ja fix. Hammerforum hier, wo einem immer so schnell geholfen wird.... Herzlichen Dank.

Zum Thema:

Ach so....alles klar...dann macht das natürlich Sinn...und schon wieder was gelernt :D
Antworten