Python Neuling verliert Objekte

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
mikaii
User
Beiträge: 1
Registriert: Montag 5. Oktober 2015, 20:32

Liebe Gemeinde, :)

Ich will mir für mein Raspberry ein Drink Spender machen. Da es vom Motorenboard die Treiber nur für Python gibt, werde ich die ganze Software in Python schreiben. Jedoch mache ich etwas konsequent falsch.

Ich habe eine Klasse Zutaten:

Code: Alles auswählen

class Zutat():
       
    def __init__(self, Name, Menge, Tempo):
        self.pName=Name
        self.pMenge=Menge
        self.pTempo=Tempo
        
    def getname(self):
        return self.pName
    
    def getmenge(self):
        return self.pMenge
    
    def gettempo(self):
        return self.pTempo
Diese sitzen in einer Liste in den Drinks:

Code: Alles auswählen

from ALS.Zutat import Zutat

class Drink():
    
    myZutaten = [Zutat]
        
    def __init__(self, NameDrink):
        self.NameDrink=NameDrink
        self.myZutaten=[]
        
    def getname(self):
        return self.NameDrink
        
    def addzutat(self, xName, xMenge, xTempo):
        print("Objekt AddZutat")
        self.NeueZutat=Zutat(xName, xMenge, xTempo)
        self.myZutaten.append(self.NeueZutat)
        
    def getzutatname(self):
        NameListe=[]
        for each_item in self.myZutaten:
            NameListe.append(each_item.getname)
        return NameListe
    
    def getzutatmenge(self):
        MengeListe=[]
        for each_item in self.myZutaten:
            MengeListe.append(each_item.getmenge)
        return MengeListe
    
    def getzutattempo(self):
        TempoListe=[]
        for each_item in self.myZutaten:
            TempoListe.append(each_item.gettempo)
        return TempoListe
Diese wiederum in Rezepte:

Code: Alles auswählen

from ALS.Drink import Drink

class Rezepte():
    
    myDrink = [Drink]
    
    def __init__(self):
        print("Rezept Objekt DONE")
        self.myDrink=[]
        
    def newdrink(self):
        print("Drink DONE")
        self.NeuerDrink=Drink("Drink1")
        self.NeuerDrink.addzutat("Zutat1", 50, 0)
        self.NeuerDrink.addzutat("Zutat2", 70, 0)
        self.NeuerDrink.addzutat("Zutat3", 80, 0)
        self.NeuerDrink.addzutat("Zutat4", 10, 0)
        self.myDrink.append(self.NeuerDrink)
Main:

Code: Alles auswählen

if __name__ == "__main__":
    from ALS.Rezepte import Rezepte
    
    a=Rezepte()
    a.newdrink()
    
    print("DrinkLaenge", len(a.myDrink))
    print("ZutatenMenge", len(a.myDrink[0].myZutaten))
    
    for each_item in a.myDrink:
        print("2", each_item.getname)
        
    for each_item in a.myDrink[0].myZutaten:
        print("3", each_item.getname)
        print("3", each_item.getmenge)
        print("3", each_item.gettempo)
    
Output:
Rezept Objekt DONE
Drink DONE
Objekt AddZutat
Objekt AddZutat
Objekt AddZutat
Objekt AddZutat
DrinkLaenge 1
ZutatenMenge 4
2 <bound method Drink.getname of <ALS.Drink.Drink object at 0x0050CD70>>
3 <bound method Zutat.getname of <ALS.Zutat.Zutat object at 0x0050C710>>
3 <bound method Zutat.getmenge of <ALS.Zutat.Zutat object at 0x0050C710>>
3 <bound method Zutat.gettempo of <ALS.Zutat.Zutat object at 0x0050C710>>
3 <bound method Zutat.getname of <ALS.Zutat.Zutat object at 0x0050CC10>>
3 <bound method Zutat.getmenge of <ALS.Zutat.Zutat object at 0x0050CC10>>
3 <bound method Zutat.gettempo of <ALS.Zutat.Zutat object at 0x0050CC10>>
3 <bound method Zutat.getname of <ALS.Zutat.Zutat object at 0x0050CD90>>
3 <bound method Zutat.getmenge of <ALS.Zutat.Zutat object at 0x0050CD90>>
3 <bound method Zutat.gettempo of <ALS.Zutat.Zutat object at 0x0050CD90>>
3 <bound method Zutat.getname of <ALS.Zutat.Zutat object at 0x0050CDF0>>
3 <bound method Zutat.getmenge of <ALS.Zutat.Zutat object at 0x0050CDF0>>
3 <bound method Zutat.gettempo of <ALS.Zutat.Zutat object at 0x0050CDF0>>

Wo liegt mein Denkfehler? Wiso kriege ich ned die Werte zurück welche ich gesetzt habe?
Ich bin am verzweifeln, kann mir jemand n wink geben?

Vielen Dank im voraus !!
BlackJack

@mikaii: Der Wink wäre es mal mit Python zu versuchen und nicht in was auch immer Du da programmierst. Die Namensgebung und die vielen trivialen Getter und das Du anscheinend erwartest das Attribute automatisch aufgerufen werden nur weil sie Methoden sind wenn man sie vom Objekt abfragt lassen vermuten das Du da eine andere Sprache in Python-Syntax gebracht hast, dich aber nicht wirklich mit Python auseinandergesetzt hast.

Hör auf jede Klasse in eine eigene Datei zu stecken, komische komplett unsinnige Präfixe wie `p`, `x`, oder `my` vor Namen zu setzen und schau Dir mal den Style Guide for Python Code an. Dann lass die ganzen `get*`-Methoden weg. Die Klassenattribute bei `Drink` und `Rezepte` machen keinen Sinn. Sollte das so eine Art ”Deklaration” sein? Denke Dir sinnvolle Namen aus. Jede Laufvariable bei Schleifen `each_item` zu nennen ist nicht sinnvoll. Selbst wenn man es generisch braucht ist das `each_` unsinig, denn das liest sich nach dem ``for`` ja noch flüssig in englisch, aber bei der Verwendung im Schleifenkörper dann schon nicht mehr. Denglisch ist auch nicht schön für Namen. Entweder oder. Es wird zu viel an die Objekte gebunden, also in verschiedenen Methoden Werte die eigentlich ganz normale lokale Werte sein sollten weil sie nichts im Zustand des Objekts verloren haben.
BlackJack

@mikaii: Das bisher gezeigte in Python:

Code: Alles auswählen

from collections import namedtuple


Ingredient = namedtuple('Ingredient', 'name amount speed')


class Drink(object):
    
    def __init__(self, name, ingredients):
        self.name = name
        self.ingredients = list(ingredients)

    def __len__(self):
        return len(self.ingredients)

    def __iter__(self):
        return iter(self.ingredients)


def main():
    drinks = [
        Drink(
            'Drink 1',
            (
                Ingredient(name, amount, speed)
                for name, amount, speed in [
                    ('Zutat 1', 50, 0),
                    ('Zutat 2', 70, 0),
                    ('Zutat 3', 80, 0),
                    ('Zutat 4', 10, 0)
                ]
            )
        )
    ]

    print('# drinks:', len(drinks))
    print('# ingredients in drink 1:', len(drinks[0]))

    for drink in drinks:
        print('2', drink.name)

    for ingredient in drinks[0]:
        print('3', ingredient.name)
        print('3', ingredient.amount)
        print('3', ingredient.speed)


if __name__ == '__main__':
    main()
Benutzeravatar
pillmuncher
User
Beiträge: 1532
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@mikaii: Der erste Denkfehler besteht darin, dass du denkst, man müsse getter verwenden, um auf Datenattribute zugreifen zu können. Warum nicht einfach so:

Code: Alles auswählen

class Zutat:

    def __init__(self, name, menge, tempo):
        self.name = name
        self.menge = menge
        self.tempo = tempo

class Drink:

    def __init__(self, name):
        self.name = name
        self.zutaten = []

    def zutat_hinzufügen(self, name, menge, tempo):
        self.zutaten.append(Zutat(name, menge, tempo))

    def namen_der_zutaten(self):
        return [zutat.name for zutat in self.zutaten]

    def mengen_der_zutaten(self):
        return [zutat.menge for zutat in self.zutaten]

    def tempi_der_zutaten(self):
        return [zutat.tempo for zutat in self.zutaten]
IMO: entweder alles auf Deutsch oder alles auf Englisch.

Außerdem: du musst Funktionen / Methoden auch aufrufen, nicht bur ihren Namen hinschreiben. Guckstu:

Code: Alles auswählen

>>> def foo():
...     return 123
... 
>>> foo
<function foo at 0x7f719a9babf8>
>>> foo()
123
Funktionen und Methoden sind in Python auch nur Objekte, aber eben solche, die man mittels eines Paares darauffolgender (und dazwischen eine Argumentliste) aufrufen kann.

Die Zeilen:

Code: Alles auswählen

myZutaten = [Zutat]
und
myDrink = [Drink]
machen nicht, was du meinst, dass sie machen. Schau mal:

Code: Alles auswählen

>>> class Bar:                                                                  
...     xs = []
... 
>>> b1 = Bar()
>>> b2 = Bar()
>>> b1.xs
[]
>>> b2.xs
[]
>>> b1.xs.append(123)
>>> b1.xs
[123]
>>> b2.xs
[123]
Wenn man einen Namen auf Klassenebene definiert, wird daraus ein Klassenattribut. Man kann darauf über jedes Exemplar der Klasse zugreifen. Alle Exemplare der Klasse teilen sich also dasselbe Attribut, und wenn es wie in meinem Beispil mutiert wird, ist das in allen Exemplaren sichtbar. Manchmal braucht man sowas, aber nicht in deinem Fall.

Das leere Klammernpaar hinter den Klassennamen bei der Klassendefinition ist unnötig. Lass es weg.

Eine Klasse pro Datei ist Java, nicht Python (ebenso wie die getter). Python ist nicht Java.

Temporäre Variablen wie self.NeueZutat und self.NeuerDrink sollten keine Attribute sein, sondern lokale Namen in der Funktion sein, wo sie verwendet werden.

Die ganze Rezepte Klasse ist ziemlich sinnfrei. Man würde eher eine Liste definieren und da dir Zutaten reinschreiben:

Code: Alles auswählen

manhattan_ice_tea = [Zutat('Bourbon', 1, '10 km/h'), Zutat('Tequila', 1, '192 bpm'), ...]
Man sieht, ich habe keine Ahnung, was Tempo bedeuten soll. Wenn ich ein Programm lese, hätte ich aber gerne, dass es mir die Bedeutung der Teile klar darlegt, und zwar am besten durch den Programmtext, und am zweitbesten durch Kommentare.

Für solche Rezept-artigen Sachen würde ich ja ein sog. Fluent Interface verwenden. Hier ein Beispiel anhand deines Beispiels:

Code: Alles auswählen

def join_without_nones(items, sep=' '):
    return sep.join(filter(None, items))


class RecipeGenerator:

    def __init__(self, name):
        self._title = 'How to make a ' + name + ':'
        self._ingredients = []
        self._directions = []

    def mix(self):
        return '\n'.join([
            self._title,
            '',
            'Ingredients:',
            '\n'.join(self._ingredients),
            '',
            'Directions:',
            '. '.join(self._directions) + '.',
        ])

    def use_cup(self, cup):
        self._directions.append(join_without_nones(
            ['Take a', cup]
        ))
        return self

    def fill_with(self, ingredient, mode=None):
        self._ingredients.append(ingredient)
        self._directions.append(join_without_nones(
            ['Fill it', mode, 'with', ingredient]
        ))
        return self

    def pour_in(self, ingredient, amount, unit, mode=None):
        self._ingredients.append(join_without_nones(
            [str(amount), unit, 'of', ingredient]
        ))
        self._directions.append(join_without_nones(
            ['Add', mode, str(amount), unit, 'of', ingredient]
        ))
        return self

    def stir(self, mode=None):
        self._directions.append(join_without_nones(
            ['Stir', mode]
        ))
        return self

    def wait(self, duration):
        self._directions.append(join_without_nones(
            ['Wait for', str(duration), 'seconds']
        ))
        return self


def make_tequila_sunrise(mixer):
    """
    Ingredients:
        0.5 oz Grenadine
        6.0 each cubes Ice
        4.0 oz Orange juice
        1.5 oz Tequila
    Directions:
        Fill a highball glass with ice. Add tequila and fill with orange juice;
        stir. Slowly pour in grenadine and let it settle. Before serving, stir
        very gently once, to create the "sunset" effect.
    """
    return (
        mixer
        .use_cup('Highball Glass')
        .fill_with('Ice')
        .pour_in('Tequila', amount=1.5, unit='oz')
        .fill_with('Orange Juice')
        .stir()
        .pour_in('Grenadine', amount=.5, unit='oz', mode='slowly')
        .wait(30)
        .stir(mode='very slowly'))


def main():
    print(make_tequila_sunrise(RecipeGenerator('Tequila Sunrise')).mix())

if __name__ == '__main__':
    main()
Ergebnis:

Code: Alles auswählen

How to make a Tequila Sunrise:

Ingredients:
Ice
1.5 oz of Tequila
Orange Juice
0.5 oz of Grenadine

Directions:
Take a Highball Glass. Fill it with Ice. Add 1.5 oz of Tequila. Fill it with Orange Juice. Stir. Add slowly 0.5 oz of Grenadine. Wait for 30 seconds. Stir very slowly.
Und um eine Maschine einen Tequila Sunrise mixen zu lassen braucht man nur die ... auszuprogrammieren:

Code: Alles auswählen

class MachineMixer:

    def __init__(self):
        ...

    def mix(self):
        return ...

    def use_cup(self, cup):
        ...
        return self

    def fill_with(self, ingredient, mode=None):
        ...
        return self

    def pour_in(self, ingredient, amount, unit, mode=None):
        ...
        return self

    def stir(self, mode=None):
        ...
        return self

    def wait(self, duration):
        ...
        return self
Und dann so verwenden:

Code: Alles auswählen

make_tequila_sunrise(MachineMixer()).mix()
In specifications, Murphy's Law supersedes Ohm's.
Antworten