Class variable in andere Class

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
lonsdejlit
User
Beiträge: 6
Registriert: Donnerstag 23. September 2021, 21:20

Hi,

Code: Alles auswählen

var1 = ("alfa","beta","gamma","delta")
var2 = (1,2,3,4,5,6,7,8,9,10)

class A:
    
    def __init__(self, var1, var2):
        self.var1 = var1
        self.var2 = var2
        
            
class B(A):
    
    def __init__(self):
        self.alle = []
        
        for i in var1:
            for j in var2:
                self.alle.append(A(i,j))

    
class C(B):
    
    def __init__(self):
        give = alle.pop(0)
1. ich versuche den ersten Elemen auf der Liste self.alle drucken . Ich bekomme aber Speicherplatz anstatt eines Elementes!
Input:

Code: Alles auswählen

x=B()
x.alle[0]
Output:

Code: Alles auswählen

<__main__.A at 0x239d66bba00>
2. Ich versuche variable "alle" aus class B in class C nutzen. Ich bekomme aber NameError anstatt eines Elementes der Liste "alle"
Input:

Code: Alles auswählen

y=C()
Output:

Code: Alles auswählen

---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-82-72c419715960> in <module>
----> 1 y=C()

<ipython-input-77-1dc500fdb289> in __init__(self)
     22 
     23     def __init__(self):
---> 24         give = alle.pop(0)
     25 

NameError: name 'alle' is not defined
Bitte um eine Info, was ich falsh mache.

[

Ich bin ziemlich neu in Python Welt. Ich mag es viel mehr als VBA aber wahrscheinlich habe ich noch was zu lernen :) Koent ihr mir eine gute Informationsquelle empfehlen mit viele Beispiele (am besten) wo ich endlich verstehen kann, wie das ganze sch... funktioniert? Python doc. ist sehr gut - aber fuer newbie nicht klar genug.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

Bevor Du mit Klassen anfängst, mußt Du Funktionen verstanden haben. Jede Funktion ist ein eigener Namensraum, in dem alles, was die Funktion an Daten braucht, über ihre Argumente kommen. Methoden einer Klasse sind dann eine spezielle Art Funktion, die zusätzlich noch das Argument `self` haben, über das man auf die Daten der Instanz zugreifen kann.

Bei Dir macht die Vererbung von A nach B keinen Sinn. Die Vererbung von B nach C ist auch noch nicht sinnvoll. Bevor Du also (nachdem Du Funktionen gut beherrschst) mit Vererbung von Klassen anfängst, solltest Du erst Klassen ohne (explizite) Vererbung lernen.

Am besten Du nimmst Dir ein konkretes Problem vor, das Du lösen möchtest, mit abstrakten A, B, C oder var1 und var2 ist der Sinn des ganzen schwer zu erraten.
lonsdejlit
User
Beiträge: 6
Registriert: Donnerstag 23. September 2021, 21:20

Danke Sirius fuer die Antwort.

Also, kurze Hintergrund:
ich möchte meine Tools wie Analytic Hierarchy Process (https://www.dawidjasinski.com/lm/) "webiger" gestalten - interaktiver machen als Tabellenkalkulationen.

Um mein Ziel zu erreichen, habe ich beschlossen, sie mit Flask auf meine Webseite hochzuladen (strategisch habe ich beschlossen, von VBA zu Python zu wechseln). Aber vorher muss ich Flask noch lernen. Aber vorher muss ich das Programm in Python schreiben. Aber vorher muss ich mich frei in Python bewegen. Deshalb habe ich vor 2 Wochen einen Python-Kurs auf codecademy und "2021 Complete Python Bootcamp From Zero to Hero in Python)" auf Udemy begonnen- hier gibt es am Ende des Kurses ein Meilenstein-Projekt, in dem man seine Fähigkeiten testen kann und hier bin ich hängen geblieben.

Ich möchte verstehen, was in meinem Code aus der Perspektive der Syntax falsch ist. Ich weiß, dass es wahrscheinlich einen besseren Weg gibt, den Meilenstein zu erreichen, aber andere Wege umgehen vielleicht meine Wissens-/Fähigkeitsdefizite. Der nicht funktionierende Code zeigt mir, dass ich etwas nicht verstehe. Ich muss verstehen, was ich falsch mache. Andernfalls wird dieses Defizit an anderer Stelle auftauchen und mir graue Haare bescheren!

Das Ziel des Meilensteins ist es, ein BlackJack-Kartenspiel zu entwickeln. Ich habe einige Klassen erstellt, die ich in Zukunft verwenden/erweitern werde.

Mein Problem: Ich möchte eine Bankklasse erstellen, die einen gemischten Kartensatz hat und eine Karte ausgeben kann. Wenn ich eine neue Instanz von Bank erstelle, erhalte ich einen Fehler:

Code: Alles auswählen

# 1. Create a card
import random
suite = ("diamonds","clubs","hearts","spades")
rank = (2,3,4,5,6,7,8,9,10,"Jack","Queen","King","Ace")
value = {2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,10:10,"Jack":10,"Queen":10,"King":10,"Ace":11}

class Card:
    
    def __init__(self, suite, rank):
        self.suite = suite
        self.rank = rank
        self.value = value[rank]
         
    def __str__(self):
        return "Suite: " + self.suite + ", Rank: " + str(self.rank) + ", Value: " + str(self.value)
        
        
# 2. Create a deck      
class Deck(Card):
    
    def __init__(self):
        self.all_cards = []
        
        for i in suite:
            for j in rank:
                unique_card = Card(i,j)
                self.all_cards.append(unique_card)
        random.shuffle(self.all_cards)

        
# 3. Create a bank
class Bank(Deck):
    
    def __init__(self):
        give_card = self.all_cards.pop(0)

# Check
new = Bank()
Der Fehler:

Code: Alles auswählen

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-129-a4b630387adf> in <module>
----> 1 new = Bank()

<ipython-input-128-8511bdc8b8f4> in __init__(self)
     33 
     34     def __init__(self):
---> 35         give_card = self.all_cards.pop(0)
     36 

AttributeError: 'Bank' object has no attribute 'all_cards'
__deets__
User
Beiträge: 14539
Registriert: Mittwoch 14. Oktober 2015, 14:29

Die Vererbung von Deck nach Bank macht keinen Sinn. Ebensowenig wie die von Card nach Deck.

Ein Deck *beinhaltet* Karten. Und eine Bank *beinhaltet* ein oder ggf mehrere Decks.

Entsprechend bringt es wenig, das hinzubiegen. Denn du lernst einen falschen Umgang mit deinen Werkzeugen.
narpfel
User
Beiträge: 645
Registriert: Freitag 20. Oktober 2017, 16:10

@lonsdejlit: In anderen Worten: Vererbung beschreibt eine ist-ein-Beziehung. Du willst aber ein hat-ein (bzw. mehrere). Das ist Komposition. Ein Deck ist-eine Karte stimmt nunmal einfach nicht.

Der konkrete Fehler hier (für die Zukunft, weil du dieses Problem ohne Vererbung lösen solltest): Du musst in den `__init__`-Methoden der Kindklassen die `__init__`-Methode der Elternklasse aufrufen, denn nur nach Ablauf der `__init__`-Methode ist ein Objekt vollständig initialisiert. Wenn `__init__` nicht aufgerufen wird, fehlen (wie du hier feststellst) die Attribute der Instanz.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Kommentare sind alle falsch. An den Stellen wird keine Karte, kein Deck und keine Bank erstellt, sondern Klassen definiert.
Konstanten werden komplett GROSS geschrieben, damit man Konstanten erkennt. Tuple sind hier falsch, das sollten Listen sein, weil gleichartige Elemente zusammengefasst werden. Listen werden an Namen im Plural gebunden, weil sie ja viele SUITES oder RANKS enthalten.
Strings stückelt man nicht mit + zusammen, sondern benutzt Formatstrings.
i und j für ein suite, bzw. rank sind schlechte Variablennamen. `new` für eine Bank ist ebenso ein falscher Name.

Nun zu den grundlegenden Verständnisproblemen: Vererbung ist eine "ist ein"-Beziehung. Ist ein Deck eine Karte? Oder besteht ein Deck nicht vielmehr aus vielen Karten.

Bei Vererbung muß man immer die __init__-Methode der Elternklasse aufrufen.
Benutzeravatar
__blackjack__
User
Beiträge: 13103
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Weitere Anmerkung: Die Konstanten `rank` und `value` (also eigentlich `RANK` und `VALUE`) enthalten redundante Daten. `RANK` ist ja komplett in `VALUE` als Schlüssel enthalten.

Beim erstellen der Liste mit den Karten würde sich eine „list comprehension“ anbieten.

`Card` als reine Datenklasse ist ja noch okay, aber `Deck` hat zu wenig Methoden um eine sinnvolle Klasse zu sein. Das gleiche gilt auch für `Bank`, wo ich noch nicht so richtig verstanden habe was die Klasse überhaupt soll.

`pop()` würde man eher nicht auf das erste Element der Liste anwenden, weil die Methode ohne Argument, oder mit -1 — was auf's gleiche hinaus läuft — effizienter ist.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
lonsdejlit
User
Beiträge: 6
Registriert: Donnerstag 23. September 2021, 21:20

__deets__, narpfel, Sirius3, __blackjack__ Danke Euch fuer hilfreichen Kommentare und Kritik!

@narpfel Ich danke Dir insbesondere für die Bennenug und Lösung des Problems!!! 🥳

BTW. Könnt ihr eine gute Studie (in einer verständlichen Sprache geschrieben) über Python empfehlen? Viele Kurse und Websites behandeln das Thema unvollständig.
Benutzeravatar
__blackjack__
User
Beiträge: 13103
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@lonsdejlit: Was auch noch unschön ist, sind verschiedene Typen für ein Attribut, also das `rank` mal eine Zahl und mal eine Zeichenkette ist.

Den Wert würde ich eher nicht in die `__str__()`-Darstellung nehmen, dafür wäre eine `__repr()__`-Darstellung nützlich wenn man sich beispielsweise beim Entwickeln oder der Fehlersuche mal eine Liste mit den Karten anzeigen lassen will.

Code: Alles auswählen

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

SUITES = ["diamonds", "clubs", "hearts", "spades"]
RANK_TO_VALUE = {
    "2": 2,
    "3": 3,
    "4": 4,
    "5": 5,
    "6": 6,
    "7": 7,
    "8": 8,
    "9": 9,
    "10": 10,
    "Jack": 10,
    "Queen": 10,
    "King": 10,
    "Ace": 11,
}


class Card:
    def __init__(self, rank, suite, value):
        self.rank = rank
        self.suite = suite
        self.value = value

    def __str__(self):
        return f"{self.rank} of {self.suite}"

    def __repr__(self):
        return (
            f"{self.__class__.__name__}"
            f"({self.rank!r}, {self.suite!r}, {self.value!r})"
        )


def main():
    deck = [
        Card(rank, suite, value)
        for suite in SUITES
        for rank, value in RANK_TO_VALUE.items()
    ]
    random.shuffle(deck)
    pprint(deck)
    print(deck.pop())


if __name__ == "__main__":
    main()
Was meinst Du mit „Studie“?
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
lonsdejlit
User
Beiträge: 6
Registriert: Donnerstag 23. September 2021, 21:20

Gott verdammt noch mal!!!

@blackjack
Sag mir bitte, dass Du Python Programmer seit 1920 bist. Ansonsten muss ich die sche.... Kurse aus dem Fenster schmeißen!!! Da die nur Zeit fressen und so ein scheiss untetichtetn dass es um jedes Wort schade! Das is fuer die Katz!

Was ich mit "Studie" meinte ist eine gute, holistische und RICHTIGE Informatiosquelle ueber die Programierung in Python, nicht wie der scheisse Codeetwas oder Udemy fuer die geistig Behinderten oder von geistig Behinderten geschrieben!

BTW. danke fuer den Code
Benutzeravatar
__blackjack__
User
Beiträge: 13103
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@lonsdejlit: Ich programmiere schon eine ganze Weile in Python. Seit 1920 nicht — dazu fehlt mir die Zeitmaschine. 😉

Vielleicht sollte ich noch mal klarstellen, dass der Sinn von den Klassen `Deck` und `Bank` in dem gezeigten Code nicht erkennbar ist, weil bisher alles ohne diese Klassen geht. Es kann sein, dass man das später mal ändern möchte und die Liste durch einen eigenen Datentyp `Deck` oder `Shoe` oder `CardMixer` ersetzen möchte. Vielleicht sogar alle drei, wenn man dem Benutzer mehrere, austauschbare Möglichkeiten anbieten möchte, wie der Dealer die Karten verwaltet. Ich bin halt nur kein Fan davon Sachen deutlich komplexer zu machen als sie gerade gebraucht werden. Wenn man sinnvolle Methoden für ein `Deck` hat, kann man auch ein Deck einführen. `shuffle()` und `draw_card()` wären da ja im Grunde schon ausreichend, und die entsprechenden ”magischen” Methoden für einen Container (`__len__()` zum Beispiel) würden auch Sinn machen.

Faustregel für sinnvolle Klassen: die müssen Zustand und Operationen darauf zusammenfassen. Also in der Regel neben der `__init__()` mindestens zwei öffentliche Operationen bieten, damit man sie nicht einfach als Funktion schreiben könnte. Ausnahmen sind reine Datenklassen, die zusammengehörende Daten zu einem Objekt bündeln, wie beispielsweise so eine einzelne Spielkarte.

Das Tutorial in der Python-Dokumentation ist von Python-Profis geschrieben. Ansonsten ist einiges aber auch gar nicht so Python-spezifisch, wie beispielsweise objektorientierte Programmierung (OOP). Und man kann natürlich auch verschiedene Ansichten haben. Es gibt bestimmt Leute, die ein Problem mit der Ansicht haben, dass man nicht alles in Klassen stopfen muss, weil Funktionen nicht OOP sind und OOP das Allheilmittel ist, also dass man sich entscheiden müsste ob man Funktionen schreibt *oder* Klassen, selbst wenn die Sprache beides bietet.

Und so einiges ist dann auch Ansichtssache. Zum Beispiel ob man `value` mit in die Zeichenkettendarstellung packt oder nicht. Ich persönlich würde es wahrscheinlich nicht einmal mit in `Card` stecken, weil man beim Blackjack sowieso eine Bewertungsfunktion braucht, denn das Ass hat ja keinen festen Wert, sondern wahlweise 1 oder 11. Man könnte auch überlegen für Werte einen eigenen Datentyp einzuführen, dann könnte man auch dem Ass einen festen Wert zuordnen. Womit das in `Card` dann für mich wieder mehr Sinn machen würde.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

lonsdejlit hat geschrieben: Donnerstag 30. September 2021, 20:11 @blackjack
Sag mir bitte, dass Du Python Programmer seit 1920 bist. Ansonsten muss ich die sche.... Kurse aus dem Fenster schmeißen!!! Da die nur Zeit fressen und so ein scheiss untetichtetn dass es um jedes Wort schade! Das is fuer die Katz!
Um im programmieren wirklich gut zu werden muss man einfach programmieren und im Laufe der Zeit sammelt man dann Erfahrungen und bekommt ein Gespür fürs programmieren generell und die Programmiersprachen die man so nutzt. Das kann ein Kurs oder ein Tutorial natürlich nicht geben, die geben dir nur den Einstieg den du brauchst um anzufangen. Ab dem Punkt muss man dann einfach machen und schauen dass man sich Feedback einholt. Letzteres z.B. im Job wenn man es professionell macht, indem man sich an Open Source Projekten beteiligt oder halt indem man hier im Forum fragen stellt.
Antworten