-1

Currently in day 15 of Angela's 100 days of python. What I understood from all the exercises and project is that variables outside the function cannot be used inside a function unless it is passed as an argument or you input "global" inside the function.

MENU = {
    "espresso": {
        "ingredients": {
            "water": 50,
            "coffee": 18,
        },
        "cost": 1.5,
    },
    "latte": {
        "ingredients": {
            "water": 200,
            "milk": 150,
            "coffee": 24,
        },
        "cost": 2.5,
    },
    "cappuccino": {
        "ingredients": {
            "water": 250,
            "milk": 100,
            "coffee": 24,
        },
        "cost": 3.0,
    }
}

profit = 0
resources = {
    "water": 300,
    "milk": 200,
    "coffee": 100,
}


def is_resource_sufficient(order_ingredients):
    """Returns True when order can be made, False if ingredients are insufficient."""
    for item in order_ingredients:
        if order_ingredients[item] > resources[item]:
            print(f"​Sorry there is not enough {item}.")
            return False
    return True


def process_coins():
    """Returns the total calculated from coins inserted."""
    print("Please insert coins.")
    total = int(input("how many quarters?: ")) * 0.25
    total += int(input("how many dimes?: ")) * 0.1
    total += int(input("how many nickles?: ")) * 0.05
    total += int(input("how many pennies?: ")) * 0.01
    return total


def is_transaction_successful(money_received, drink_cost):
    """Return True when the payment is accepted, or False if money is insufficient."""
    if money_received >= drink_cost:
        change = round(money_received - drink_cost, 2)
        print(f"Here is ${change} in change.")
        global profit
        profit += drink_cost
        return True
    else:
        print("Sorry that's not enough money. Money refunded.")
        return False


def make_coffee(drink_name, order_ingredients):
    """Deduct the required ingredients from the resources."""
    for item in order_ingredients:
        resources[item] -= order_ingredients[item]
    print(f"Here is your {drink_name} ☕️. Enjoy!")


is_on = True

while is_on:
    choice = input("​What would you like? (espresso/latte/cappuccino): ")
    if choice == "off":
        is_on = False
    elif choice == "report":
        print(f"Water: {resources['water']}ml")
        print(f"Milk: {resources['milk']}ml")
        print(f"Coffee: {resources['coffee']}g")
        print(f"Money: ${profit}")
    else:
        drink = MENU[choice]
        if is_resource_sufficient(drink["ingredients"]):
            payment = process_coins()
            if is_transaction_successful(payment, drink["cost"]):
                make_coffee(choice, drink["ingredients"])

I tried to look at her solution and saw that one of her function is using the dictionary resources that is not declared inside the function nor passed as an argument. I am not very good in english that's why I am having a hard time searching in the internet what I specifically want to understand. Can someone enlighten me with this topic please.

NOTE: it is not advised to use global

My code:

(my understanding is that you can never use variables outside the function if it is not either set to global or passed as an argument)

def use_resources(user_order, machine_menu, machine_resources):
    """Deduct the resources needed for the user's order and returns the current resources of the machine after the
    user's order. """
    for menu_ingredients_key in machine_menu[user_order]["ingredients"]:
        # print(menu_ingredients_key)  # REPRESENT KEY water, coffee
        # print(menu[order]["ingredients"][menu_ingredients_key])  # REPRESENT VALUES [50,18]

        for resources_key in machine_resources:
            if resources_key == menu_ingredients_key:
                machine_resources[menu_ingredients_key] -= menu[user_order]["ingredients"][menu_ingredients_key]
    print(f"Here is your {user_order} ☕. Enjoy! Come again :)")

How can the function use the resources that was declared outside the function and not passed as an argument?

def make_coffee(drink_name, order_ingredients):
    """Deduct the required ingredients from the resources."""
    for item in order_ingredients:
        resources[item] -= order_ingredients[item]
    print(f"Here is your {drink_name} ☕️. Enjoy!")
Kejer
  • 25
  • 5
  • 3
    *What I understood from all the exercises and project is that variables outside the function cannot be used inside a function unless it is passed as an argument or you input "global" inside the function.* – that is just not true. – mkrieger1 Dec 01 '22 at 22:16
  • 3
    It helps to write small examples that just show the problem. You could write a small function that uses a global variable then post that instead of this large amount of code. – tdelaney Dec 01 '22 at 22:16
  • 3
    You can freely use global variables inside a function, without any explicit `global` declaration, as long as the function only *reads* the variable; in fact, you do this *all the time* in Python (the use of `print()` in that function is another example of the same thing). Assigning a new value to a variable is what makes it a local variable, unless declared `global`. – jasonharper Dec 01 '22 at 22:18
  • Does this answer your question? [Using global variables in a function](https://stackoverflow.com/questions/423379/using-global-variables-in-a-function) – mkrieger1 Dec 01 '22 at 22:19
  • @jasonharper I think this is where the confusion starts, in angela's code she is using resources[item] -= order_ingredients[item] , doesnt this mean that she's assigning a new value to the variable? – Kejer Dec 01 '22 at 22:29
  • 1
    @Kejer: no, `resources` is exactly the same object before and after. Its *contents* have been modified, but that's not what the rule applies to. – jasonharper Dec 01 '22 at 22:50

2 Answers2

1

would like to add an answer here posted by John in the udemy QnA section:

Lists and dictionaries are mutable. That means that you can add and remove elements from the list/dictionary and it still remains the same list/dictionary object. It is not necessary to create a new list/dictionary in this case.

Almost all other Python objects are immutable. That means that once created, they cannot be altered in any way. For example, if a is an integer, then when you do a += 1 an entirely new integer object is created.

Global variables can be read anywhere in the file, including inside functions.

The rule is that functions are not allowed to create new global objects without you giving specific permission using the global keyword.

To illustrate the point, let's see what the memory location of the object is:

my_list = [1, 2, 3]
print(id(my_list), my_list)
my_list += [4]  # my_list is the same list object
print(id(my_list), my_list)
 
print()
 
my_int = 123
print(id(my_int), my_int)
my_int += 1  # my_int is a new integer object
print(id(my_int), my_int)

See Mutable vs Immutable Objects in Python

Kejer
  • 25
  • 5
0

Consider a simpler example

resources = {
    "water": 300,
    "milk": 200,
    "coffee": 100,
}

def test(item, val):
    resources[item] -= val

test("water", 5)

This program works. test updates resources even though resources isn't a parameter. Python needs to know which variables used in a function are local and which are not. The rule is simple: variables that are assigned in a function are local to the function. The global keyword breaks that rule and lets you assign variables in the global scope.

In this example

def test2():
    var1 = "foo"
    print(var2)
    global var3
    var3 = "baz"

var1 is assigned in the function, so python makes it local. var2 is referenced in the function but is not assigned, so python looks into the global scope for its value. var3 is assigned, but its also declared global, so the local variable rule is broken and the assignment will go to the global scope.

tdelaney
  • 73,364
  • 6
  • 83
  • 116