Handling variable scope in a python script - python

I am learning to code in Python.
I am creating a program that will perform unit conversions, however I have been stuck on this error for a good while:
NameError: name 'ini' is not defined
Here is the code:
a = ("Distance")
b = ("Time")
c = ("Volume")
d = ("Temp")
e = ("Weight")
def conversion_type(first):
if first == ("Distance"):
ini = input("How great a distance am I converting?\n")
elif first == ("Time"):
ini = input("How much time am I converting?\n")
elif first == ("Volume"):
ini = input("How great a volume am I converting?\n")
elif first == ("Temp"):
ini = input("How many degrees am I converting?\n")
elif first == ("Weight"):
ini = input("How much weight am I converting?\n")
else:
print("That not was an afformentioned dimension you dolt.")
def variable_type_converter(ini):
ini = float(ini)
print ("\n Welcome to the Convert-O-Matic\n==============================\n")
print ("I support the following dimensions:\n")
print ("%s, %s, %s, %s, and %s," % (a,b,c,d,e))
first = input("What kind of conversion would you like to do?\n")
conversion_type(first)
variable_type_converter(ini)
print("==========================================")

ini is not declared in the global scope, only inside of functions (it is used in conversion_type() and therefore implicitly declared there, and it is an argument to variable_type_converter()). But since it was not declared in the global scope, it does not exist there. If you want to set the value of ini in conversion_type() and have the value be usable elsewhere, declare a value for ini somewhere before you call conversion_type().
This answer has a good summary of Python's scoping rules.
Update: As MadWombat points out in comments, your variable_type_converter() function doesn't do anything. It takes one argument, called ini, it casts it as float and reassigns back to ini, but then it doesn't return a value. So when the variable_type_converter() function exits, the value is discarded and and the float cast is never used.

When defining your variables you should use a = "Distance". You should also check the conversion type using first == "Distance"
ini is not defined, is occurring because the function conversion_type(first) is returning a value that is not stored. Try:
# get conversion type
conversion_type_chosen = conversion_type(first)
# calculate value
converted_value = variable_type_convertor(conversion_type_chosen)
# print result
print "result: {}".format(converted_value)

Related

Python user defined list not being recognised within a while true loop

Thanks firstly for bearing with me as a relative newcomer to the world of Python. I'm working on a simple set of code and have been racking my brain to understand where I am going wrong. I suspect it is a relatively simple thing to correct but all searches so far have been fruitless. If this has been covered before then please be gentle, I have looked for a couple of days!
I'm working on the following and after catching and correcting a number of issues I suspect that I'm on the last hurdle:-
def main():
our_list = []
ne = int(input('How many numbers do you wish to enter? '))
for i in range(0, (ne)): # set up loop to run user specified number of time
number=int(input('Choose a number:- '))
our_list.append(number) # append to our_list
print ('The list of numbers you have entered is ')
print (our_list)
main()
while True:
op = input ('For the mean type <1>, for the median type <2>, for the mode type <3>, to enter a new set of numbers type <4> or 5 to exit')
import statistics
if op == "1":
mn = statistics.mean(our_list)
print ("The mean of the values you have entered is:- ",mn)
if op == "2":
me = statistics.median(our_list)
print ("The median of the values you have entered is:- ",me)
if op == "3":
mo = statistics.mode(our_list)
print ("The mode of the values you have entered is:- ",mo)
if op == "5":
main()
else:
print("Goodbye")
break`
For some reason the appended (our_list) is not being recognised within the while true loop rendering the statistics calculation void. Any steer would be really appreciated as to where I am missing the obvious, thanks in advance.
Cheers
Bryan
I'm not sure exactly what you mean by "not being recognized", but our_list is a local variable inside main, so it can't be used anywhere but inside main.
So, if you try to use it elsewhere, you should get a NameError.
If your code actually has a global variable with the same name as the local variable that we aren't seeing here, things can be more confusing—you won't get a NameError, you'll get the value of the global variable, which isn't what you want.
The best solution here is to return the value from the function, and then have the caller use the returned value. For example:
def main():
our_list = []
ne = int(input('How many numbers do you wish to enter? '))
for i in range(0, (ne)): # set up loop to run user specified number of time
number=int(input('Choose a number:- '))
our_list.append(number) # append to our_list
print ('The list of numbers you have entered is ')
print (our_list)
return our_list
the_list = main()
while True:
op = input ('For the mean type <1>, for the median type <2>, for the mode type <3>, to enter a new set of numbers type <4> or 5 to exit')
import statistics
if op == "1":
mn = statistics.mean(the_list)
print ("The mean of the values you have entered is:- ",mn)
if op == "2":
me = statistics.median(the_list)
print ("The median of the values you have entered is:- ",me)
if op == "3":
mo = statistics.mode(the_list)
print ("The mode of the values you have entered is:- ",mo)
if op == "5":
the_list = main()
else:
print("Goodbye")
break
There are other options—you could pass in an empty list for main to fill, or use a global variable (or, better, a more restricted equivalent like an attribute on a class instance or a closure variable), or refactor your code so everyone who needs to access our_list is inside the same function… but I think this is the cleanest way to do what you're trying to do here.
By the way, this isn't quite the last hurdle—but you're very close:
After any mean, median, or mode, it's going to hit the "Goodbye" and exit instead of going back through the loop. Do you know about elif?
You mixed up '5' and '4' in the menu.
If the user enters 2 and 3 and asks for the mode, your code will dump a ValueError traceback to the screen; probably not what you want. Do you know try/except?
That's all I noticed, and they're all pretty simple things to add, so congrats in advance.
The issue is that our_list was defined in the main() function, and is not visible outside of the main() function scope.
Since you're doing everything in one chunk, you could remove line 1 and 6, taking the code from your main() function and putting it on the same indentation level as the code which follows.
This seems to be because you defined our_list within the main() function. You should probably define it as a global variable by creating it outside the main() function.
You could also put the while loop inside a function and pass in our_list as a parameter to the list.

Specific example: Is it possible to aviod the use of a global variable?

I have the following program and the variable(dictionary) in question is player_info that stores player information (name and goals). In order to solve the error that results currently, I simply need to make player_info a global variable, but I was wondering if stackoverflow experts could suggest or discuss the possibility of alternate ways of solving this problem WITHOUT the use of global variables.
Code
#FOOTBALL COACH app
#The program allows a user to enter a number of players (their names and goals scored) and then search for a player, returning their average goals for the three matches
import sys
def main():
mainmenu()
def mainmenu():
print("=====WELCOME to the MAIN MENU=============")
print("""
1..........Add New Players & Goals
2..........Search by Players
3..........Quit
=========================================
""")
choice=int(input("Enter choice:"))
if choice==1:
addplayers()
elif choice==2:
searchplayer(player_info)
elif choice==3:
sys.exit()
else:
print("You must make a valid choice - 1, 2 or 3")
def addplayers():
player_info= {} #create a dictionary that stores the player name: player goals
num_players = int(input("Please enter number of players you wish to enter:"))
print ("You are entering %s players" %num_players)
player_data = ['Match 1 goals : ', 'Match 2 goals : ', 'Match 3 goals : ']
for i in range(0,num_players):
player_name = input("Enter Player Name :")
player_info[player_name] = {}
for entry in player_data:
player_info[player_name][entry] = int(input(entry)) #storing the marks entered as integers to perform arithmetic operations later on.
mainmenu()
def searchplayer():
print("===============SEARCH by player: Calculate average goals==================")
name = input("Player name : ")
if name in player_info.keys():
#print student_info
print ("Average player goals : ", str(sum(player_info[name].values())/3.0))
else:
print("Please enter a valid player name:")
main()
As mentioned, I am aware that re-writing this in the addplayer() sub would fix the problem:
global player_info
player_info = {} #create a dictionary that stores the player name: player goals
...I am looking for ways to solve the problem WITHOUT the use of global variables.
Update:
One answer below using return player_info is what I would like to go with, but it doesn't quite work yet. Also, I need to return to the main menu each time a player is added, not quite sure how to do this, without a mainmenu call each time. Any suggestions? https://repl.it/JRl5/1
You can use return inside your function to avoid using global variables. A simple example is shown below:
def addplayers():
player_info= {}
name = input("Enter Name: ")
test = int(input("Enter a number: "))
player_info[name] = test
return player_info
player_info = addplayers()
If you then wanted to use this in another function you would just pass in the dictionary as an argument to that function:
def searchplayers(player_info):
print (player_info)
Note: An interesting answer on "Why are global variables evil?"
Edit:
Your addplayers() was calling mainmenu() which itself was being calling within mainmenu(). This is a recursive function and it might be best to avoid these unless there's a good reason for having it. I would put the contents of mainmenu inside a while loop until some condition is met. The complete code is shown below (I have removed the main function as it wasn't really doing anything):
def mainmenu():
stop = False
while stop == False:
print("=====WELCOME to the MAIN MENU=============")
print("""
1..........Add New Players & Goals
2..........Search by Players
3..........Quit
=========================================
""")
choice=int(input("Enter choice:"))
if choice==1:
player_info = addplayers()
elif choice==2:
searchplayer(player_info)
elif choice==3:
print ("Exit the main menu")
stop = True
else:
print("You must make a valid choice - 1, 2 or 3")
def addplayers():
player_info= {} #create a dictionary that stores the player name: player goals
num_players = int(input("Please enter number of players you wish to enter:"))
print ("You are entering %s players" %num_players)
player_data = ['Match 1 goals : ', 'Match 2 goals : ', 'Match 3 goals : ']
for i in range(0,num_players):
player_name = input("Enter Player Name :")
player_info[player_name] = {}
for entry in player_data:
player_info[player_name][entry] = int(input(entry)) #storing the marks entered as integers to perform arithmetic operations later on.
return player_info
def searchplayer(player_info):
print("===============SEARCH by player: Calculate average goals==================")
name = input("Player name : ")
if name in player_info.keys():
#print student_info
print ("Average player goals : ", str(sum(player_info[name].values())/3.0))
else:
print("Please enter a valid player name:")
mainmenu()
Store everything related to the game in a data structure, for example a dictionary, and pass it along in all functions where it can be updated as needed. Write a function "newgame" that creates this structure and initialises it.
In a way, this is object-oriented programming without using Python's syntax for classes and objects. Probably, you will learn these later in your class / tutorial.
Firstly, it is always possible to avoid using global variables. Secondly, global variables are possibly a misnomer in Python; global stores the variable in the local globals, which is typically the local module. That avoids a large part of the problem languages like C have with globals, in that they collide; Python has a namespace per module. For a simple script, where there is only one context, that might be fine.
Another namespace you might use is that of a particular object, using a class. This might look like:
class Game:
def mainmenu(self,...):
self.addplayers()
def addplayers(self):
self.player_info = {}
With that sort of code, whoever instantiates Game can make multiple instances, each passed as self when used. This is in large part syntactic sugar for a similar form of mutable state passing:
def mainmenu():
state={}
addplayers(state)
def addplayers(state):
state['player_info'] = {}
For some forms of programming, immutable state is far preferable (in particular, multithreading where data is shared, or to keep a log where you can undo steps). That's done similarly but you make a new state for each call:
def mainmenu():
state = {}
state = addplayers(state)
def addplayers(oldstate):
newstate = oldstate.copy()
newstate['player_info'] = {}
return newstate
Python isn't designed for this and doesn't really have a mode to keep you from inadvertently modifying mutable types. Some types can be converted to similar types that are immutable, like frozenset or tuple.
One of the weirder hacks we can do is calling a Python function with a different set of globals than it normally has. This can be abused to take your existing functions, global statements and all, and have them use a different variable:
fakeglobals = mainmenu.__globals__.copy()
exec(addplayers.__code__, fakeglobals)
Your original code has calls back and forth between functions, though, and each of those will reset their globals per that __globals__ attribute.
Your code also implements a loop using tail recursion. This is not optimized in Python and will run out of stack space eventually. In languages where tail recursion is optimized, you can pass along state as arguments continously and need not return it.

If else statement , global name not defined

def vel(y,umax,r,Rmax):
vel_p=umax*(1-(r/Rmax)**2)
if r<50:
r=50-y
else:
r=y-50
return 'the value of velocity in cell is %r,%r,%r,%r'%(umax,r,Rmax,vel_p)
def main ():
y=(input('enter y'))
a=(input('enter the umax'))
#b=(input('enter the r'))
b=(r)
c=(input('enter the Rmax'))
print(vel(a,c,b,y))
main()
i do not understand where i should put r it gives me an error global variable r not defined
As already mentioned in the comments, try to use "good" (=readable) variable names since this helps to reduce confusion.
The conversion from string to float should be made robust against non-numerical input with try...except, so I put that in a separate function.
Usually you don't want a function to return a string with all your calculated values inserted, but the "raw" values. The printing of those values should normally be done somewhere else.
In the comments you mention you "need to get the value of r from y, if i do not put that in comments it is taking my value of r and will not calculate from the if r statement", but your function vel() uses r to calculate vel_p in the very first line. The variable r is an argument to the function, so it has to come from somewhere. Either you let the user input it like all the other values, or you have to define it somewhere else. If you do that globally, have a look at Vipin Chaudharys answer.
My suggestion, if you want the user to input r:
def vel(y, u_max, r, r_max):
# You use the value of r here already!
vel_p=u_max*(1-(r/r_max)**2)
# Here you change r, if r is less than 50.
# You are using r again, before assigning a new value!
if r<50:
r=50-y
else:
r=y-50
# I use the preferred .format() function with explicit field names
# \ is used to do a line-break for readability
return 'The value of velocity in cell is umax: {value_u_max}, \
r: {value_r}, Rmax: {value_r_max}, vel_p: {value_vel_p}.'.format(
value_u_max=u_max, value_r=r,value_r_max=r_max, value_vel_p=vel_p)
# Helper function to sanitize user input
def numberinput(text='? '):
while True:
try:
number=float(input(text))
# return breaks the loop
return number
except ValueError:
print('Input error. Please enter a number!')
def main():
y=numberinput('Enter y: ')
u_max=numberinput('Enter the umax: ')
r=numberinput('Enter the r: ')
r_max=numberinput('Enter the Rmax: ')
print(vel(y, u_max, r, r_max))
main()
Notice, that the input value of r is used to do the calculation. Then it is changed depending on y, and the new value gets printed.
In your main method , you assigned b = (r) whereas you never specified what is r , so if you have variable r in your global scope then the first line in the main method should be
def main():
global r
# Now you can use your r
by doing so , you called your variable r in your method.
Hope it helps :)

How would I assign this variable?

I barely started this python tutorial and I don't understand how I would set this variable(total)
Think about it this way: Your Variable meal now stores two meals plus the tax for both of them. The only thing that is missing is the tip.
Now you can create this
total = meal + tip
if meal or tip is changing, the result of total would change too. But you need to be careful to not get confused with this behaviour.
Variables are bound to their object content if you assign them directly like
variable = 1234
if you now got another variable that stores the same content you would (normally) not write this again:
vari2 = 1234
but instead:
vari2 = variable
The interesting about that is, if you print
print variable
print vari2
you get the same results as expected. But if you change your first variable after all this code, there will be a difference! You can try to run this last code piece to understand what i mean:
vari = 1234 #Integer variable
print "Vari: %r" % vari
varia = vari #The Variable varia is bound to the CONTENT of vari not to the Name vari!
print "varia from vari: %r\n... -> New vari " % (varia)
vari = 42 # Now if you change vari, the content of varia is still the same!
print "Varia %r from vari %r" % (varia, vari)
This behaviour is very useful to store the original starting value of a variable. So you can keep track how the variable changed over time.
I suggest to play a little with variables and print to get a better understanding.
You have already used the same concept in the previous lines.
total = meal + (meal * tax)

Python quiz difficulty function

I'm new to Python and am really struggling to create a function for my quiz: I have created a while loop to offer different levels to the quiz player before they receive any questions. The while loop works on its own, but when i try to turn it into a function, the level variable stops working (returning only empty brackets outside of the code).
This is the while loop:
request_count = 0
level = ()
global_string_list = ()
while request_count < 1:
print user_level_options
level_request = raw_input("> ")
if level_request == "1":
level = string_1
global_string_list = string_1_list
if level_request == "2":
level = string_2
global_string_list = string_2_list
if level_request == "3":
level = string_3
global_string_list = string_3_list
if level_request == False:
print "Please try again."
request_count = request_count + 1
Could anybody please give me pointers on how to turn this into a function please?
Thank you!!
The level variable returns empty brackets because the level within the function is a local variable; completely different from the level you declared outside the function, which is outside the function's scope. This outside variable was hidden behind the local level variable, so all your assignments within the function were made to the local level and did not affect the level on the outside at all.
You have to either declare level as a global variable explicitly (generally considered bad practice) or you return your local level from the function. So perform all the changes you want in your function and then use the return statement like this:
def user_select(): # your function
#......your code goes here
return level
level = user_select() # the level variable within your function is now in this variable
I try to give you a jump start. But I won't give you the final solution.
In general, to make a function meaningful, you have to know why you need it.
I give you some example of the benefit using function.
Removal of repetitive code. (DRY, don't repeat yourself)
Identify a Pattern, and extract into a separate function. In your example, the repeating behavior can be seen near the condition inside the loop. (right after the raw_input function).
That is: if level_request == <something> then do <some-work>.
Thus, one improvement could be:
def updateLevel(level_request):
# Think about how you can determine the level without if-else
level = <?level?>
global_string_list = <?global_level|level?>
such that you only need to call:
while request_count < 1:
print user_level_options
level_request = raw_input("> ")
updateLevel(level_request)
if level_request == False:
print "Please try again."
request_count = request_count + 1
Abstraction
This is a very important topic in Computer Science and General Programming. You have to understand more to be able to appreciate the importance of abstraction.
E.g.
def askLevel():
print(user_level_options)
return raw_input("> ")
def displayError():
print("Please try again")
def incrementRequestCounter():
request_count += 1
such that you just need to call:
while request_count < 1:
level_request = askLevel()
updateLevel(level_request)
if level_request == False:
displayError()
incrementRequestCounter()
Note: in strict functional programming, function is a mapping of (input)->(output). And the function does not modify any global state (immutability). And all function must return a value. But in practice, we normally use a procedure instead. (procedure is a sequence of instruction). And def in python is both procedure and function.

Categories

Resources