2

I'm trying to generate a dictionary with a lambda value and for some reason it isn't working as it should.

The bit in my code that is broken is:

def market():
    clear()

    # Creates the dictionary for use in menu.display
    stock = {}
    for obj in stuff.instances:
        stock[f"{obj.name:<10} {obj.qty:<5} ${obj.price}"] = lambda: buy(obj) # <<<<<<<< this is whats broken
    stock["Go back"] = lambda: main()

    menu.display(menu("Here's what's in stock: \n   "+"Name       Qty   Price", stock))  # creates a menu object with the stock and displays and runs the choice

So what's happening is: I am iterating through a list of objects created by a class, I am then adding to a dictionary, originally empty, with the key being some information about the object, and the value being a lambda function which calls another function called "buy()" with an argument of the object (This is what doesnt work.)

For some reason, for all choices, it will only "buy" the last object in the list. I don't believe this is an issue with the menu.display function itself as later, I add another key called "Go back" with a value of "lambda: main()" which works perfectly fine.

I have tested around and doing stuff such as replacing the lambda function with something like "obj.name" and printing it and that works fine. Its purely just that and I'm completely lost why.

Obviously this code won't work on it's own so I'm also going to include another snippet with all the relevant stuff to make it work and so that u can see whats kinda meant to be going on. I've put a comment saying "<<<<<< this is whats broken" so u dont miss the original thing, its near the end.

import random
from time import sleep
from replit import clear

# variables

money = 10000
inv = 0


# classes

class menu:
    def __init__(self, text, menuDict):
        self.text = text
        self.menu = menuDict


    # Displays the menu, gets the choice, and runs the relevant lambda
    def display(self):
        choice = " "

        while ord(choice) < 65 or ord(choice) > 65 + len(self.menu):  # checks if thing typed in is a valid choice
            clear()

            letter = 65  # chr(65) = A

            # displays menu
            print(self.text)
            for key in self.menu.keys():
                print("{}: {}".format(chr(letter), key))
                letter += 1
            choice = input().upper()

            if len(choice) == 0 or len(choice) > 1:  # ord() doesn't like strings of 0 length and strings > 1 length
                choice = " "

        choice = ord(choice) - 65
        list(self.menu.values())[choice]()  # Executes the chosen lambda within the dict



class stuff:
    instances = []  # When you call drugs.instances, it will return a list of all 
                    # the instances within the class
    qtyVariation = 0.7

    def __init__(self, name, basePrice, maxPriceRise, minPriceFall, baseQty):
        self.name = name
        self.basePrice = basePrice
        self.maxPriceRise = maxPriceRise
        self.minPriceFall = minPriceFall
        self.baseQty = baseQty

        self.price = basePrice
        self.qty = 0
        self.owned = 0
        stuff.instances.append(self) # adds the obj just made to the class's list of instances


    # probably irrelevant, same with method underneath
    @classmethod
    def randomise_prices(cls):
        for obj in cls.instances:
            if random.randint(0, 1):  # Higher
                obj.price = round(obj.basePrice * random.uniform(1, obj.maxPriceRise))
            else:  # Lower
                obj.price = round(obj.basePrice * random.uniform(obj.minPriceFall, 1))
    
    @classmethod
    def randomise_qtys(cls):
        for obj in cls.instances:
            obj.qty = round(obj.baseQty * random.uniform(1 - cls.qtyVariation, 1 + cls.qtyVariation))


thing1 = stuff(name="thing1"
            , basePrice=120
            , maxPriceRise=1.4
            , minPriceFall=0.8
            , baseQty=5)
thing2 = stuff(name="thing2"
            , basePrice=50
            , maxPriceRise=2
            , minPriceFall=0.8
            , baseQty=10)
thing3 = stuff(name="thing3"
            , basePrice=400
            , maxPriceRise=1.2
            , minPriceFall=0.7
            , baseQty=3)


# funcs

def market():
    clear()

    # Creates the dictionary for use in menu.display
    stock = {}
    for obj in stuff.instances:
        stock[f"{obj.name:<10} {obj.qty:<5} ${obj.price}"] = lambda: buy(obj) # <<<<<<<< this is whats broken
    stock["Go back"] = lambda: main()

    menu.display(menu("Here's what's in stock: \n   "+"Name       Qty   Price", stock))  # creates a menu object with the stock and displays and runs the choice


def buy(item):
    global money, inv
    if item.qty <= 0:
        print(f"Not enough {item.name} in stock.")
        sleep(2)
    elif item.price > money:
        print("You cannot afford that.")
        sleep(1)
    else:
        money -= item.price
        item.qty -= 1
        item.owned += 1
        inv += 1
    market()


def main():
    clear()
    print("hello i am main func")
    sleep(1)
    market()


if __name__ == "__main__":
    stuff.randomise_prices()
    stuff.randomise_qtys()
    main()

Please ignore my frankly shitty code lol, first proper time trying to work with classes and all that so I dont know too much about them.

Anything would appreciated such as telling me I'm dumb and "wtf is happening in ur code" and that I should just approach it completely differently, and by all means do (I know I'm sorry! very new) but at least tell me what I'm doing wrong and how I could go about correcting it. Thanks!

mkj05
  • 61
  • 2

0 Answers0