Auf einzelnes Element in einem Array 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
Arlic
User
Beiträge: 7
Registriert: Montag 22. April 2019, 15:32

Hallo zusammen,

ich bin neu hier und möchte mich Hobbymäßig etwas mit Programmieren beschäftigen.
Bei dem Projekt, was ich mir vorgenommen habe, wird ein Array mit Werten gefüllt. Ich verstehe nur nicht, wie ich auf einen einzelnen Wert zugreifen kann.
Im Moment stehen (bis auf den counter am Anfang) noch die gleichen Werte drin.

Wie kann ich z.B. beim zweiten Eintrag den Wert 350 abfragen und benutzen? Gedacht ist es wie folgt:

[1, 350, 0, 100]
1= Stelle im Array
350= wird später x-Koordinate
0= wird später y-Koordinate
100= wird später weitere Variable

Code: Alles auswählen

werte = []

counter = 1

def function(liste):
    print(liste)
    #Hier würde ich gerne auf einzelne Werte der Liste zugreifen
     

while counter <= 10:
    werte.insert(len(werte)+1,[counter,350,0,100])
    function(werte)
    counter += 1
            
        

quit()
Danke
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn du Koordinaten speichern willst, solltest du gleich Tupel dazu nehmen:

[(100, 30), ...]

Und der Zugriff auf Sequenzen wie Listen oder Tupel erfolgt mit [index]. Also zB

Code: Alles auswählen

x, y = koordinaten[0] 
Wenn ein Element so wie oben ein Tupel ist. Sonst eben nacheinander mit zwei Zugriffen.
nezzcarth
User
Beiträge: 1633
Registriert: Samstag 16. April 2011, 12:47

Was du da in deinem Code hast, sind keine Arrays, sondern Listen. (Die Unterscheidung ist m.M.n. wichtig, weil Python auch Arrays hat, die Arrays heißen, die man aber eher selten braucht). Man kann auf die einzelnen Elemente einer Liste (und auch eines Tupels) mit folgender Notation zugreifen:

Code: Alles auswählen

sequence[index]
Die Indexierung beginnt bei 0. Näheres zu dieser Syntax gibt es im Python-Tutorial (das ist auf Listen übertragbar):
https://docs.python.org/3/tutorial/intr ... ml#strings

Das quit() am Ende wird nicht benötigt. Der Befehl ist für den interaktiven Interpreter gedacht und funktioniert nur aus technischen Gründen auch in Skripten.
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Arlic: Nachdem die technische Seite geklärt ist: Nein, das willst Du nicht machen.

Als erstes mal ist die ``while``-Schleife eigentlich eine ``for``-Schleife. Dann ist das `insert()` ein komplett bekloppt ausgedrücktes `append()`.

Des weiteren macht es wenig Sinn den Index (oder etwas konstant Versetztes) mit in die Liste aufzunehmen, denn man kann sich das entweder beim iterieren generieren oder aus dem Index den man für den Zugriff verwenden berechnen.

Dann erstellt man nicht auf Vorrat Listenelemente mit irgendwelchen Werten die erst später durch die tatsächlichen Werte ersetzt werden. Und für den seltenen Fall wo das doch mal gemacht wird, würde man eher `None`-Werte in die `werte`-Liste stecken, damit das deutlich wird. In dem Fall liesse sich die Liste auch durch Multiplikation statt mit einer Schleife erstellen.

Aber auch die Listenelemente von `werte` sind so keine gute Idee. Listen sind für Werte gedacht bei denen jedes Element die gleiche Bedeutung hat, und wo nicht der Index die Bedeutung bestimmt. Dafür verwendet man eher Tupel. Wenn man aber nicht alle Werte des Tupels bei jedem Zugriff braucht, und man sie deshalb auf sinnvolle Namen entpacken kann, führt so etwas schnell zu magischen Indexwerten, die Programme schwerer verständlich machen. Also `collections.namedtuple()`. Oder gleich Klassen. Insbesondere wenn es sich nicht um Wertobjekte handelt, sondern um welche die verändert werden können sollen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Arlic
User
Beiträge: 7
Registriert: Montag 22. April 2019, 15:32

Danke für eure Antworten. Die haben mir sehr geholfen - glaube ich. Wird jetzt ausprobiert =)

Tupel sind meine ich unveränderbar, darum geht das im Programm leider nicht.

Ich habe mit dem Beispiel versucht mein Problem deutlich zu machen, mein Project ist etwas größer und hat schon einige Zeilen beisammen. Ich wollte nicht, dass sich da jeder durchfuchsen muss.
__blackjack__ hat geschrieben: Montag 22. April 2019, 18:10
Als erstes mal ist die ``while``-Schleife eigentlich eine ``for``-Schleife. Dann ist das `insert()` ein komplett bekloppt ausgedrücktes `append()`.
Stimmt, danke :-)
__blackjack__ hat geschrieben: Montag 22. April 2019, 18:10 Des weiteren macht es wenig Sinn den Index (oder etwas konstant Versetztes) mit in die Liste aufzunehmen, denn man kann sich das entweder beim iterieren generieren oder aus dem Index den man für den Zugriff verwenden berechnen.

Dann erstellt man nicht auf Vorrat Listenelemente mit irgendwelchen Werten die erst später durch die tatsächlichen Werte ersetzt werden. Und für den seltenen Fall wo das doch mal gemacht wird, würde man eher `None`-Werte in die `werte`-Liste stecken, damit das deutlich wird. In dem Fall liesse sich die Liste auch durch Multiplikation statt mit einer Schleife erstellen.
Muss ich mir nochmal nachschauen. Den Index wollte ich drinhaben, um mir das löschen zu vereinfachen. Sollte sich der als unnötig erweisen, fliegt der wieder raus.
In dem eigentlichen Programm sind die Werte der Liste später natürlich durch Variabelen gefüllt.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du kannst wenn es sein muss natuerlich auch eine Liste von Listen machen. Aber ganz ehrlich: wenn du beim programmieren so denkst, das du einzelne Eintraege in der Liste nachtraeglich manipulierst, machst du es falsch. Das ist sehr unueblich, meistens baut man einfach neue Listen. Und WENN kannst du genauso gut ein ganzes Tupel austauschen, das in der Liste steht.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Arlic: wenn Du nicht Dein eigentliches Problem beschreibst, ist es natürlich schwierig bis unmöglich zu helfen. Das was Du gezeigt hast, würde man normalerweise nie in Python schreiben, egal um welches Problem es sich handelt.

Man versucht zu vermeiden Listen oder Listen in Listen zu verändern, weil sonst Änderungen an der einen Stelle das Verhalten an einer anderen verändern kann und es so zu schwer zu findenden Fehlern kommen kann.
Arlic
User
Beiträge: 7
Registriert: Montag 22. April 2019, 15:32

Danke nochmal.

Ihr habe mir weitergeholfen! Der Teil, der mir vorher probleme machte, klappt jetzt wunderbar.

Ob das am Ende so funktioniert, wie ich es mir denke, wird sich noch zeigen. Wenn es das tut, poste ich es gerne komplett hier und lasse mir erklären, wie ich es besser/richtiger gemacht hätte =)
Zuerst möchte ich mir aber selber eine Lösung erarbeiten.
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Mal als Beispiel wie man das mit unveränderlichen Werten durch ersetzen mit einem neuen, angepassten Wert machen kann:

Code: Alles auswählen

#!/usr/bin/env python3
from pprint import pprint

from attr import attrib, attrs, evolve


@attrs(frozen=True)
class PleaseFindABetterNameForMe:
    x = attrib(default=0)
    y = attrib(default=0)
    also_find_a_better_name_for_me = attrib(default=None)


def function(items):
    item = items[2]
    items[2] = evolve(item, x=item.x + 42, y=item.y + 23)


def main():
    items = [PleaseFindABetterNameForMe(350, 0, 100)] * 10
    pprint(items)
    function(items)
    pprint(items)


if __name__ == '__main__':
    main()
Es wird das externe `attr`-Package verwendet, was einem einiges an Arbeit abnimmt, wie beispielsweise eine sinnvolle `repr()`-Darstellung für die Exemplare.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Arlic
User
Beiträge: 7
Registriert: Montag 22. April 2019, 15:32

Die Namen scheinen dich wirklich zu stören oder xD

Vielen Dank für das Beispiel! Das könnte damit besser funktionieren. Ich habe das attr Packet schon mal installiert.

Meine Lösung funtioniert... nicht! Evtl. habe ich mir das ganze auch komplett falsch vorgestellt. Ich habe das erste mal versucht mit einer Liste zu arbeiten. Auf jeden Fall hattet ihr recht, es war keine gute Idee!
Ich habe trotzdem mal angehängt worum es geht (und ja das mit der for-Schleife habe ich tatsächlich nicht verbessert sondern sogar nochmal gemacht :oops: )

Ich versuche etwas in der Art wie Asteroids zu programmieren. Dazu gibt es mit Sicherheit Lösungen, die besser als meine sind aber bevor ich mir irgendwo was abgucke, will ich selber probieren. Da ich nun nach geraumen Stunden nicht zu einem funktionierenden Ergebnis gekommen bin, poste ich nochmal. Den Teil mit dem Schiff + Steuerung habe ich rausgenommen, kann ihn aber gerne auch posten, wollte den code aber kürzer halten.

Würdet ihr es über attr lösen?

Die Idee war, dass ich je nach Schwierigkeit verschieden viele Asteroiden auf dem Bildschirm habe. Die sollten ersmal am rechten rand erscheinen und durchfliefen. Dazu wollte ich folgendermaßen vorgehen:

1. createAst
Füllt die Liste mit Asteroiden, die Koordinaten am rechten Bildschirmrand bei zufälligem y-Wert mit Geschwindigkeit 3 erhalten.

2. asteroids
Die Liste wird durchgegangen und auf dem Bildschirm angezeigt

3. astMove
Die Liste wird durchgegangen und der x-Wert ersetzt und erneut angezeigt

Code: Alles auswählen

import pygame
import time
import random
pygame.init()

difficulty = 10

display_width = 800
display_height = 600
gameDisplay = pygame.display.set_mode((display_width,display_height))

astImg =  pygame.image.load('asteroidC.png')

def astMove(asteroid):
    xAst, yAst, speed = asteroid[len(asteroid)-1]
    xAst -= speed
    counter = len(asteroid)
    while counter > 0:
        asteroid.insert(counter,[xAst, yAst, speed])
        counter -=1
    pygame.display.update()
    
def asteroids(astListe):
    quantity = len(astListe)
    while quantity > 0:
        xAst, yAst, speed = astListe[len(astListe)-1]
        gameDisplay.blit(astImg,(xAst, yAst))        
        quantity -= 1
    pygame.display.update()

def createAst(asteroid):
    counterAst = 0
    while counterAst < difficulty:
            #in die Lister asteroid wird an die Stelle counterAst,[x-Koordinate, y-Korrdinate, Geschwindigkeit]) ein Asteroide gesetzt
            asteroid.insert(counterAst,[display_width,random.randint(0,600),3])
            counterAst += 1
        
    return asteroid

def game_loop():
    asteroid = []

    gameExit = False
    while not gameExit:
        
        
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                crashed = True
                
    if len(asteroid) < difficulty:
            createAst(asteroid)
            
    gameDisplay.fill(0,0,0)

    astMove(asteroid)
    asteroids(asteroid)

game_loop()
pygame.quit()
quit()   
    
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Bei dem `quit()` ganz am Ende habe ich ein Dejavu. :-)

Und ja, Namen sind wichtig! Das ist keine Kosmetik, das ist wichtig um Quelltext zu verstehen und nicht selten auch um szu merken, wenn man etwas was man da programmiert, selbst nicht ganz verstanden hat.

Auf Modulebene sollte nur Code stehen, der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahemen: Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).

`asteroid` (Einzahl) ist kein passender Name für eine Liste mit Asteroiden (Mehrzahl).

`gameExit` wird definiert und dann nur in der nächsten Zeile verwendet, nirgends sonst. Wenn man den Wert dort einsetzt, steht da ``while not False:``, was man direkter und kürzer als ``while True:`` schreiben kann.

`crashed` wird nirgends verwendet. An der Stelle möchte man vielleicht die Funktion verlassen.

In `astMove()` willst Du nicht das Display aktualisieren. Die Funktion sollte die Asteroiden bewegen, *das* gibt der Name her. Mehr nicht.

Hier ist auch wieder so eine komische ``while``-Schleife mit einem `insert()` in die Liste. Du willst doch alle Asteroiden bewegen und nicht am Ende eine Liste mit doppelt so vielen Asteroiden haben. Wie schon gesagt wurde, verändert man Listen in der Regel auch nicht. Man erstellt einfach eine *neue* Liste, mit den veränderten Asteroiden. Und die gibt man dann als Ergebnis an den Aufrufer zurück.

`ast` ist auch eine blöde Abkürzung. Vor allem weil diese Abkürzung in der Informatik eine Bedeutung hat: „abstrakt syntax tree”. Wenn Du `asteroid` (oder `asteroids`) meinst, dann schreib das auch, schon ist keiner verwirrt was das wohl bedeuten mag.

`createAst()` sollte die Liste der Asteroiden gar nicht übergeben bekommen. Die wird doch von der Funktion überhaupt nicht benötigt um ihre Aufgabe zu erfüllen. Die braucht nur den Asteroiden erstellen und zurück geben. Wobei sie das bei Dir ja sogar schon tut, nur das Du damit dann gar nichts machst.

`asteroids` ist kein guter Name für eine Funktion. Das ist keine Tätigkeit. `blit_asteroids()` wäre beispielsweise passender.

``asteroids[len(asteroids)-1]`` ist eine umständliche Schreibweise für ``asteroids[-1]``. Und mir ist nicht ganz klar warum Du immer den letzten Asteroiden für jeden Asteroiden in der Liste blitten willst‽ Und schon wieder so ein ``while`` mit manuellem Zähler. Das ist einfach nur eine ``for``-Schleife über die Asteroiden. Dabei kann man die auch gleich auf Namen entpacken.

Und das `Surface` auf dem geblittet werden soll, muss als Argument übergeben werden.

Zwischenstand (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
import random
import time

import pygame

DISPLAY_WIDTH = 800
DISPLAY_HEIGHT = 600
ASTEROID_IMAGE = pygame.image.load('asteroidC.png')
DIFFICULTY = 10


def moved_asteroids(asteroids):
    return [(x - speed, y, speed) for x, y, speed in asteroids]
    

def create_asteroid():
    return (DISPLAY_WIDTH, random.randint(0, DISPLAY_HEIGHT), 3)


def blit_asteroids(screen, asteroids):
    for x, y, _ in asteroids:
        screen.blit(ASTEROID_IMAGE, (x, y))


def game_loop(screen):
    asteroids = list()

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return
                
    asteroids = moved_asteroids(asteroids)
    
    while len(asteroids) < DIFFICULTY:
        asteroids.append(create_asteroid())

    screen.fill(0, 0, 0)
    blit_asteroids(screen, asteroids)
    pygame.display.update()


def main():
    pygame.init()
    try:
        screen = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT))
        game_loop(screen)
    finally:
        pygame.quit()


if __name__ == '__main__':
    main()
Man will da vielleicht noch beim `moved_asteroids()` einbauen das Asteroiden die aus dem Bildschirm geflogen sind, nicht in die Rückgabeliste aufgenommen werden.

Das Problem bei unvollständigen Problembeschreibungen ist, das man hier jetzt Beispielsweise vorher nicht wusste, dass es die `Rect`-Klasse und die Sachen aus dem `sprites`-Modul gibt. Man würde hier nämlich dann für die Position ein `Rect`-Objekt nehmen, das Bild und die Position in ein `Sprite`-Objekt stecken, und die Asteroiden in eine Sprite-Gruppe.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Arlic: Ups, da ist bei mir der Code in `game_loop()` teilweise falsch eingerückt. Dafür ist bei Dir (und damit auch bei mir) der `fill()`-Aufruf falsch und führt zu einem `ValueError`.

Hier das ganze mal mit `Sprite` und `Group` aus `pygame.sprite` und mit einer zufällig gewählten Geschwindigkeit und mit Asteroiden die gelöscht werden wenn sie links rausgeflogen sind, damit neue rechts erzeugt werden können:

Code: Alles auswählen

#!/usr/bin/env python3
import random

import pygame
from pygame.sprite import Group, Sprite
from pygame.time import Clock

DISPLAY_WIDTH = 800
DISPLAY_HEIGHT = 600
ASTEROID_IMAGE = pygame.image.load('asteroidC.png')
DIFFICULTY = 10
BLACK = (0, 0, 0)


class Asteroid(Sprite):
    
    def __init__(self, x, y, speed):
        Sprite.__init__(self)
        self.image = ASTEROID_IMAGE
        self.rect = ASTEROID_IMAGE.get_rect()
        self.rect.midleft = (x, y)
        self.speed = speed
    
    def update(self):
        self.rect.move_ip(-self.speed, 0)
        if self.rect.right <= 0:
            self.kill()


def game_loop(screen):
    clock = Clock()
    asteroids = Group()

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return
                
        asteroids.update()
        
        while len(asteroids) < DIFFICULTY:
            asteroids.add(
                Asteroid(
                    DISPLAY_WIDTH,
                    random.randint(0, DISPLAY_HEIGHT),
                    random.randint(1, 3)
                )
            )

        screen.fill(BLACK)
        asteroids.draw(screen)
        pygame.display.update()
        
        clock.tick(30)


def main():
    pygame.init()
    try:
        screen = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT))
        game_loop(screen)
    finally:
        pygame.quit()


if __name__ == '__main__':
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Arlic
User
Beiträge: 7
Registriert: Montag 22. April 2019, 15:32

Hui vielen Dank!

Das muss ich mir jetzt mal in Ruhe anschauen. In den nächsten Tagen komme ich da wegen der Arbeit nicht zu. Ich melde mich aber bestimmt wieder und werde zwischenzeitlich auch an der Namensgebung arbeiten =)
Arlic
User
Beiträge: 7
Registriert: Montag 22. April 2019, 15:32

__blackjack__ hat geschrieben: Donnerstag 25. April 2019, 09:10 @Arlic: Ups, da ist bei mir der Code in `game_loop()` teilweise falsch eingerückt. Dafür ist bei Dir (und damit auch bei mir) der `fill()`-Aufruf falsch und führt zu einem `ValueError`.
Was meinst du mit "fill()-Aufruf" stehe hier auf dem Schlauch.
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Arlic: Ein `fill()` kommt doch nur einmal im Code vor: ``gameDisplay.fill(0,0,0)`` – den meine ich. Mein erster Code war ja ungetestet, da hatte ich das einfach so übernommen, aber da fehlen Klammern weil die Farbe *ein* Argument ist und nicht drei.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Arlic
User
Beiträge: 7
Registriert: Montag 22. April 2019, 15:32

Heute habe ich endlich nochmal Zeit und hab mich an die Sprites herangewagt. Einiges habe ich verstanden. Unklar ist mir der Startaufruf:
Ich vermute:
Self wird übergeben an Sprite und ein neues Objekt initialisiert?

Code: Alles auswählen

Sprite.__init__(self)
Jetzt liegt mein Problem darin, mit Sprites sinnvollen Code zu erstellen. Hast du zufällig eine Empfehlung für ein Tutorial?

Ich dachte mir z.B. wenn zwei Asteroiden sich treffen könnten die sich gegenseitig zerstören. Hier hab ich schon Probleme, wie ich anfangen soll.

1.Ich war auf https://www.pygame.org/docs/ref/sprite. ... rite.Group und habe mir die Befehle angesehen.
2. Da gibt es diverse Befehle die pygame.Rect.collideIRGENDWAS heißen und für das Problem in Frage kämen
3. Ich verstehe nicht, wie ich die Befehle einbauen kann soll

Code: Alles auswählen

def update(self):
        self.rect.move_ip(-self.speed, 0)
        if pygame.Rect.colliderect(self.rect):
            self.kill()
        if self.rect.right <= 0:
            self.kill()
Ich dachte mir, dass es so funktionieren könnte. --> TypeError: Argument must be rect style object
bei self.rect.left
TypeError: descriptor 'colliderect' requires a 'pygame.Rect' object but received a 'int'

Irgendwie verstehe ich das nicht. Wie kann ich ihm denn ein "rect style object" zuweisen?
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Arlic: Du hast offensichtlich Probleme mit objektorientierter Programmierung, denn `pygame.Rect.colliderect()` ist nicht dazu gedacht es auf dem Datentyp/der Klasse aufzurufen, sondern auf einem Exemplat von `Rect`. Was sollte die Zeile denn überhaupt bewirken? Die Methode prüft ob das Rechteck mit einem *anderen* Rechteck kollidiert – man braucht da also *zwei* Rechtecke für. Du hast aber nur `self.rect` – womit soll das kollidieren?

Zudem würde ich bei Sprites dann auch eher die Kollisionsfunktionen aus dem `sprite`-Modul verwenden statt die `Rect`-Methoden. Der Test wäre auch auf einer höheren Ebene besser aufgehoben, wo man an alle Asteroiden leicht heran kommen kann – eher nicht in einem individuellen Asteroiden-Sprite.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten