0

I am having some trouble for carrying my variable from a sub-routine into another sub-routine.

Here is the code:

def loop1():
    try:
        age=int(input("How old are you? "))
    except ValueError:
        print ("Please enter a numerical integer of your age. For example: 19 ")
        print("")
        loop1()
    if age>0:
        program()

def program():
    print("")
    print("[1] - Knife / Spray Paint / Lottery Ticket ")
    print("[2] - Alcohol / Tobacco ")
    print("[3] - Anything else ")
    print("")
    loop2()

def loop2():
    try:
        item=int(input("What would you like to buy from the options above? "))
        print("")
    except ValueError:
        print ("Please enter a numerical integer of your item. For example (if you wanted to buy alcohol): 2 ")
        print("")
        loop2()
    if item>0:
        validation()

def validation(): 
    if item == 1 and 16>age :
        print("Sale Denied - Item cannot be sold to Under 16s. ")
    elif item == 1 and 16<age:
        print("Sale Accepted. ")

    elif item == 2 and 18>age:
        print("Sale Denied - Item cannot be sold to Under 18s. ")
    elif item == 2 and 25>age>18:
        print("Check ID before selling alcohol - Challenge 25. ")
    elif item == 2 and 18<age:
        print("Sale Accepted. ")

    elif item == 3:
        print("Sale Accepted. ")

loop1()

Here is the outcome:

How old are you? 21

[1] - Knife / Spray Paint / Lottery Ticket 
[2] - Alcohol / Tobacco 
[3] - Anything else 

What would you like to buy from the options above? 2

Traceback (most recent call last):
  File "D:/Shop Program.py", line 48, in <module>
    loop1()
  File "D:/Test.py", line 9, in loop1
    program()
  File "D:/Shop Program.py", line 17, in program
    loop2()
  File "D:/Shop Program.py", line 28, in loop2
    validation()
  File "D:/Shop Program.py", line 33, in validation
    if item == 1 and 16>age :
NameError: global name 'item' is not defined

As you can see from the error message above it is saying that global name 'item' is not defined. I have tried to place global item, above def vaildation():, but I still get the same error.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Anonymous
  • 49
  • 1
  • 1
  • 9

3 Answers3

2

Rather than using global, which is a bad practice (in Python and everywhere else), explicitly pass item from loop2 into validation:

def loop2(age):
    ...
    if item > 0:
        validation(item, age)
                 # ^ pass it here

def validation(item, age): 
             # ^ receive it here
    if item == 1 and 16 > age:
        ...

Note that I have done a similar thing with age, which should be passed in when loop2 is called. Using recursion for input validation isn't ideal; see Asking the user for input until they give a valid response for an alternative approach.

Community
  • 1
  • 1
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
0

Forgive me if you already know this, but there is another way to get the item into the validate sub-routine that might work better for you. You can "pass in" a variable to a subroutine (also called methods or functions). Variables that you "pass in" to a subroutine are called arguments. To use subroutine arguments, you have to do 2 things:

  1. Define the arguments in the subroutine definition
  2. Specify the variable to "pass in" when you call the subroutine

So for you, defining the argument item on your validate routine would look like this:

def validate(item):
    if item == 1 and 16>age :

See how I stuck item between the parenthesis.

And then, all you have to do is "pass in" the item to the validate function:

def loop2():
try:
    item=int(input("What would you like to buy from the options above? "))
    print("")
except ValueError:
    print ("Please enter a numerical integer of your item. For example (if you wanted to buy alcohol): 2 ")
    print("")
    loop2()
if item>0:
    validation(item)

Notice how I put item between the parenthesis on the call to the validation subroutine on the last line.

Hope that helps

Colin Nichols
  • 714
  • 4
  • 12
0

Apart from your original question: There is the possibility for an infinite recursion in loop1 and loop2 if the exception is risen, as the functions call themselves.

A better way is to use a loop (while True: ...) with a break after successful conversion.

Also, it is bad practice in every programming language to chain functions the way you did. That is an even worse idea than using goto and is commonly called spaghetti code. Better is to have a single main function which calls the functions in succession and passes results of a former function as arguments to the next function:

age = get_age()
item = get_item()
if age < 16 and item == ...:
    print("Not allowed")
...

A better approach would be to use a dict of { "item" : minimal_age }:

items = { "butter": 0 , "knife": 16, "booze": 18, "beer": 17 }

age = ...
item = get_item(items)    # the list can be built automatically from dict
if age < items[item]:
    print("Too young for", item)
else:
    purchase(item)

That would avoid a loooooong list of if .. elif .. tests. If the items are identified by strings instead of numbers, that would add to readability. In general, in Python numberincal values should only be used where they are "natural", otherwise use the appropriate type like strings, sets, etc. Note that this is different from other languages (e.g. C), where string handling is quite uncomfortable and expensive.

too honest for this site
  • 12,050
  • 4
  • 30
  • 52