Wie auf Instanzattribute zugreifen ?

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
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo,
wie greife ich richtig auf Instanzattribute zu ? Für mich wäre alles richtig, doch sollte man die erste Möglichkeit gar nicht machen und die zweite wird eine Bastelei sein. Bei der dritten und vierten Möglichkeit sehe ich keinen Unterschied. Mit einer dict würde es mir persönlich besser gefallen, da man dann über die keys (z.B.: "head") "verständlich" zugreifen kann. Mit dem __is__ geht nicht, da das Objekt(Snake) ja weiter bestehen bleibt. doch so etwas wäre auch schön.

Code: Alles auswählen

        
#! /usr/bin/env python
# -*- coding: utf-8

class Snake(object):
    def __init__(self):
        self.head = (0, 1)
        self.tails = ((2, 3), (4, 5), (6, 7), (8, 9), (10, 11))
        self.food = (12, 13)
        self.positions = (self.head, self.tails, self.food)
        self.alive = True
        
    def __iter__(self):
        return iter(self.positions)
        
    def __getitem__(self, index):
        return self.positions[index]
        
    #def __is__(self):
        #return self.alive
        
    def get_positions(self):
        return (self.head, self.tails, self.food)
        
        
def main():
    snake = Snake()
    #1
    head, tails, food = snake.head, snake.tails, snake.food
    print head, tails, food
    #2
    head, tails, food = snake
    print head, tails, food
    #3
    head, tails, food = snake[0], snake[1], snake[2]
    print head, tails, food
    #4
    head, tails, food = snake.get_positions()
    print head, tails, food
    
if __name__ == "__main__": main()
Gruß Frank
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Nummer 1 ist der einzige Weg mit dem man tatsaechlich auf die Attribute zugreift.

2-4 haben (normalerweise) den Zweck ueber ein Objekt zu iterieren.
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo cofi,

wie macht man es jetzt aber richtig ? Würde ich Ui machen und würde zur Darstellung die Werte der einzelnen Instanzattribute benötigen, dann wäre es also egal wie ich es mache ? Ich hatte eigentlich gedacht, dass so tiefe Eingriffe wie bei Nr. 1 nicht gemacht werden sollten. Die Liste self.positions erzeuge ich ja nur, um auf alle zugreifen zu können. Ich könnte auch gleich den Weg 1 oder 4 nehmen. Wozu brauche ich die anderen Möglichkeiten ?

Gruß Frank
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Was meinst du mit "tiefe Eingriffe"? Du meinst, dass jemand der `Snake` benutzen will, kennen muss, dass es diese Attribute gibt? Aber das gilt fuer jeden Ansatz.

Die anderen Moeglichkeiten haben eben ihren Einsatzzweck wenn du eine Art Container hast (bspw listenartig).
Um bei `Snake` zu bleiben und du eine Moeglichkeit anbieten willst ueber die Glieder zu iterieren:

Code: Alles auswählen

class Snake(object):
    def __init__(self):
        self.parts = []
        ...
        
    def __iter__(self):
        return self.parts
        
    def __getitem__(self, i):
        return self.parts[i]
        
snake = Snake()
for part in snake:
    cook(part)
#cook the head twice
cook(snake[0])
Weil es mir gerade auffaellt: `snake.tails` sollte kein Tupel sein, sondern eine Liste von Tupeln (evtl willst hier statt Tupeln aber sowieso eine Koordinaten Klasse). Wenn du ein Tupel nicht durch ein `collections.namedtuple` ersetzen koenntest (jede Position hat eine bestimmte Bedeutung), dann solltest du kein Tupel benutzen (es sei denn du brauchst die Unveraenderlichkeit).
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo cofi,

hier mal ein Snake-Game - hatte ich schon im Showcase. Man sollte doch hier besser die Logik von der UI trennen. Bedeutet in diesem Script wäre die Trennung nicht gegeben und die mittels "__iter__" oder "___getitem___" besser - oder ?

Code: Alles auswählen

#! /usr/bin/env python
# -*- coding: utf-8

FIELD_WIDTH = 400
FIELD_HEIGHT = 400

import Tkinter as tk
from functools import partial
import random

class Snake(object):
    def __init__(self, field_width, field_height, number_of_tails, 
        tails_width):
        self.field_width = field_width
        self.field_height = field_height
        self.number_of_tails = number_of_tails
        self.tails_width = tails_width
        self.x = 0
        self.y = 0
        self.tails = list()
        self.food = list()
        self.positions = list()
        self.alive = True
        self.head = (self.field_width / 2, self.field_height / 2)
        for tail in xrange(self.number_of_tails):
            x, y = self.head
            self.tails.append((x, y))
        self.food = self.get_food()

    def set_move_direction(self, x, y):
        if self.x == y:
            self.x, self.y = x, y
        elif self.y == x:
            self.x, self.y = x, y
            
    def set_position(self):
        head_x, head_y  = self.head
        new_head_x, new_head_y = head_x + self.x, head_y + self.y
        self.head = (new_head_x, new_head_y)
        if (len(self.tails) > self.number_of_tails 
            and (new_head_x, new_head_y) in self.tails
            or new_head_x == -self.tails_width
            or new_head_x == self.field_width 
            or new_head_y == -self.tails_width
            or new_head_y == self.field_height):
                self.alive = False
        elif self.head == self.food:
            for tail in xrange(self.number_of_tails):
                self.tails.append(self.head)
            self.food = self.get_food()
        self.tails.pop()
        self.tails.insert(0, self.head)
        
    def get_food(self):
        food_positions = list()
        for x in xrange(self.tails_width, self.field_width, self.tails_width):
            for y in xrange(self.tails_width, self.field_height, 
                self.tails_width):
                if (x, y) not in self.tails:
                    food_positions.append((x, y))
        return random.choice(food_positions)


class SnakeGui(tk.Canvas):
    SPEED = 50
    FIELD_COLOR = "lightgreen"
    NUMBER_OF_TAILS = 5
    TAILS_WIDTH = 5
    TAILS_COLOR = "green"
    FOOD_COLOR = "red"
    SNAKE_UP = "<KeyPress-Up>"  
    SNAKE_DOWN = "<KeyPress-Down>"
    SNAKE_RIGHT = "<KeyPress-Right>"
    SNAKE_LEFT = "<KeyPress-Left>"
    
    def __init__(self, root, field_width, field_height):
        tk.Canvas.__init__(self, root, width = field_width, 
            height = field_height, bg = self.FIELD_COLOR)
        self.root = root
        self.field_width = field_width
        self.field_height = field_height
        self.control = ((self.SNAKE_UP, 0, -self.TAILS_WIDTH),
                        (self.SNAKE_DOWN, 0, self.TAILS_WIDTH),
                        (self.SNAKE_RIGHT, self.TAILS_WIDTH, 0),
                        (self.SNAKE_LEFT, -self.TAILS_WIDTH, 0))
        for button, x, y in self.control:
            self.root.bind(button, partial(self.move_snake, x, y))
        self.after_id = None
        self.start_game()

    def start_game(self):
        self.snake = Snake(self.field_width, self.field_height, 
            self.NUMBER_OF_TAILS, self.TAILS_WIDTH)
        if self.after_id:
            self.after_cancel(self.after_id)
        self.run()
        
    def move_snake(self, x, y, event):
        self.snake.set_move_direction(x, y)

    def run(self):
        if self.snake.alive:
            self.delete("snake_tail", "snake_food")
            self.snake.set_position()
            for x, y in self.snake.tails:
                self.create_oval(x, y, x + self.TAILS_WIDTH, y 
                    + self.TAILS_WIDTH,tag="snake_tail", 
                    fill=self.TAILS_COLOR)
            x, y = self.snake.food
            self.create_oval(x, y, x + self.TAILS_WIDTH, y + self.TAILS_WIDTH, 
                fill=self.FOOD_COLOR, tag="snake_food")
            self.after_id = self.after(self.SPEED, self.run)
        else:
            self.delete("snake_food", "snake_tail")
            self.start_game()
        
def main():
    root = tk.Tk()
    gui = SnakeGui(root, FIELD_WIDTH, FIELD_HEIGHT)
    gui.pack()
    root.mainloop()
if __name__ == "__main__": main()
Gruß Frank
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ohne mir den Code angeguckt zu haben, brauchst du eigentlich nur folgendes zu beachten: Deine Domänen-Komponenten dürfen *keinerlei* Abhängigkeit zu GUI Komponenten haben, sondern nur anders herum.

Deine Klasse zur Modellierung einer Schlange darf also niemals irgendwie Zugriff auf ein GUI Objekt benötigen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Mit anderen Worten: Wenn du den nicht-GUI Code alleine benutzen kannst, hast du alles richtig gemacht.
In dem Fall: Kannst du "Snake" von hand im Interpreter "spielen" ohne die GUI Klasse zu benutzen?

Ich hab den Code nur ueberflogen, aber das scheint mir der Fall zu sein.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

@cofi: Man muss es ja nicht unbedingt spielen können, denn Konsolen Ausgaben gehören ja auch unabhängig vom reinen Domänencode implementiert. Letztlich wäre das auch eine Art UI, die man oftmals gar nicht braucht. Das Testen von Teilen in einer Shell reicht eigentlich aus.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Das ist aber genau das was ich meine, darum auch die Anfuehrungszeichen. Wenn ich die Schlange bewegen, essen und was sonst noch alles machen kann, dann kann ich das ganze Spiel eben auch "spielen".

Soll nicht heissen, dass es so auch Spass machen muss ;)
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo und Danke,

Snake läuft auch ohne Gui.

Code: Alles auswählen

class SnakeKonsole(object):
    NUMBER_OF_TAILS = 5
    TAILS_WIDTH = 5
    
    def __init__(self, field_width, field_height):
        self.field_width = field_width
        self.field_height = field_height
        self.start_game()

    def start_game(self):
        self.snake = Snake(self.field_width, self.field_height, 
            self.NUMBER_OF_TAILS, self.TAILS_WIDTH)
        self.run()
        
    def move_snake(self, x, y):
        self.snake.set_move_direction(x, y)

    def run(self):
        while self.snake.alive:
            self.move_snake(0, - self.TAILS_WIDTH)
            self.snake.set_position()
            time.sleep(0.05)
        else:
            print "game over"
            self.start_game()
        
def main():
    SnakeKonsole(FIELD_WIDTH, FIELD_HEIGHT)
if __name__ == "__main__": main()
Snake sollte die Gui "steuern" und nicht umgekehrt ?

Gruß Frank
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Nein, du machst es genau richtig. Die GUI leitet die Eingaben weiter und steuert so das Modell.
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo cofi,

ja meinte schon, dass die Gui auch der Controller ist, doch alles andere macht Snake selbst und die Gui zeigt nur an und reagiert auf das Verhalten (z.B.: "game over" etc.) von Snake.

Gruß Frank
Antworten