Calculations - Using functions - python

I am a bit confused over this.
I have a function. Inside that function it asks a number of questions, one is the following based on the number of gardens they have - so if they say they have 2 gardens it will ask this question twice and should add the 100 twice to the calculation:
gardens = int(input("How many gardens do you have: "))
def gard():
calc = 0
gardener = input("Do you need a gardener? Y or N ")
if gardener == "Y" or gardener == "y":
calc = calc + 100
else:
calc = calc + 0
for i in range(gardens):
gard()
How do I keep a running total outside of the function? When I put print(calc) inside the function it just displays 100 each time they say Y but doesn't add it together.
Edited to include updated code:
The eMake section (the IF statement) returns a value - but it only ever returns the first in the calculation at the end?
Also struggling to do the area section since there are numerous ws. It only stores the last value for the variable.
noGard = int(input("Enter number of gards which require cleaning: "))
#Defining variables
Calc = 0
Area = 0
emCalc = 0
#Room information
def GInfo():
global Calc
global Area
gName = input("Enter gard name: ")
noW = int(input("How many w are in the "+gName + "? "))
#Repeats the questions for each W
for i in range(noW):
Height = float(input("What is the w height of in metres? "))
Width = float(input("What is the w width in metres? "))
Area = Height * Width
#Asks if w needs to be removed
w = input("Does w need removing? Y or N ")
if w == "Y" or w == "y":
Calc = Calc + 70
else:
Calc = Calc + 0
print (" ")
#Returns the values
return Calc
return Area
#Calculate Sarea
#Identifying e
def e():
global emCalc
#eMake
eMake = input("What make of e - HH or NN? ")
if eMake == "HH" or "hh":
emCalc = emCalc + 200
elif eType == "NN" or "nn":
emCalc = emCalc + 50
else: print("You have entered an invalid e make")
#Returns the values
return emCalc
#Repeats the g information questions for each g
for i in range(noGard):
GInfo()
# Runs the E function
e()
#Print total without VAT
total = Calc + emCalc
print(total)
print(Area)

Your function should return the calculated value.
def gard():
...
return calc
total = 0
for _ in range(gardens):
total += gard()
print 'Total: ', total

The whole point of functions, really, is that they take parameters and return values. (Some languages, although not Python, refer to functions that don't do this as "procedures".)
That is what you need to do here: your gard function needs to return the value of calc. You probably don't want to actually do the addition inside the function itself, but if you did, you would also need to accept the current value of calc as a parameter, which you would pass in from your for loop.

Functions, in the strictest sense, do not have state. When writing functional programs, one typically aims to keep their functions pure, meaning that the result of the function does not depend on anything but its inputs and does not cause observable side effects.
But Python is not a purely functional language. It is an object-oriented procedural language which models functions as objects, and objects can be stateful. So you can do what you're aiming to, if you don't take the word "function" too literally.
The Right Thing™
Create a class which models your data and the operations on it:
>>> class F(object):
... def __init__(self):
... self.x = 0
... def f(self):
... self.x += 1
... return self.x
...
>>> my_f = F()
>>> my_f.f()
1
>>> my_f.f()
2
Fun and naughty ways
Add state to the function object, taking advantage of the fact that function bodies aren't executed until the function is called:
>>> def f():
... f.x += 1
... return f.x
...
>>> f.x = 0
>>> f()
1
>>> f()
2
If you want to do this transparently (that is, make it so that you don't have to add this state to the function right after defining it) you can close over the state by having a function create a function:
>>> def g():
... def func():
... func.x += 1
... return func.x
... func.x = 0
... return func
...
>>> f = g()
>>> f()
1
>>> f()
2
To take it a step further, create a decorator so you don't have to do any assignments after defining the function at all:
>>> def with_x(func):
... func.x = 0
... return func
...
>>> #with_x
... def f():
... f.x += 1
... return f.x
...
>>> f()
1
>>> f()
2
Or you can just use global to let a function refer to something outside of its local scope, not taking advantage of the fact that functions are objects:
>>> x = 0
>>> def f():
... global x
... x += 1
... return x
...
>>> f()
1
>>> f()
2
>>> x
2
Updates for your edit
Since you went with global I'll first refer you to a good question that explains global. Using global variables in a function other than the one that created them
Now, as for your particular problems:
The eMake section (the IF statement) returns a value - but it only ever returns the first in the calculation at the end?
Sure, there are a couple problems here and one of them is definitely a common one for beginners. or takes higher precedence than == so your condition parses like this:
if (eMake == "HH") or ("hh"):
This gets people all the time. In Python, if a value isn't a boolean and you put it in a conditional statement, it gets evaluated as a boolean using a series of truthiness rules. In this case a non-empty string is considered True so you are basically saying if (eMake == "HH") or True.
To fix this, fix the right-hand side of the condition:
if (eMake == "HH") or (eMake == "hh"):
By the way, you probably meant elif (eMake == "NN") or (eMake == "nn"): instead of elif eType == "NN" or "nn": because you never defined eType (and for the reason above, too.) If you type nn there you'll get an exception.
Also struggling to do the area section since there are numerous ws. It only stores the last value for the variable.
This is because you repeatedly assign to the same variable with Area = Height * Width. Since Area is global, it's the same variable every time you call GInfo(). If it wasn't global it would be a new variable every time you called the function, but then you would need to return it and assign the return value to a variable in order to save the value. Otherwise it would disappear since it was never assigned to anything.
Now, I don't know what you are trying to do with the areas you're calculating. Do you want to keep them separate or sum them together?
If you want to keep them separate, you'll need to use a data structure. In this case, you'd definitely want to use a list. Using the append() method of lists, you can add an item to the list. So it would look something like this:
areas = [] # empty list
def GInfo():
global areas
# the stuff before the loop
for i in range(noW):
Height = float(input("What is the w height of in metres? "))
Width = float(input("What is the w width in metres? "))
areas.append(Height * Width)
# the stuff after the loop
If you want to sum them together, just make sure you add each individual area calculation to the previous result, just like you did with Calc:
Area += Height * Width
One more thing: your GInfo() function is only returning Calc and not Area as well. Functions can only return one value. In the mathematical sense, a function is a many-to-one mapping between two sets. So in Python, a function ends at the return statement. Nothing else gets executed after that.
In order to get both the value of Calc as well as the value of Area from the return value of GInfo(), you will have to return a data structure. Usually this would be a tuple.
return (Calc, Area)
But your code doesn't assign the return value of GInfo() to anything. Instead, it uses the global declaration to change the value of the global variables. So there shouldn't be an issue here.

Related

How to pass information back and forth between scripts

I'm trying to pass information back and forth between 2 scripts. In one, we obtain a user input, in the other some modification is done to the user input, then back to the first one, we print out that modification.
#del2
def fun():
return int(user_input)+1
#script to run
user_input=input('some number')
from del2 import fun
print(fun())
So when we run our script, the user gives some input, the next line then runs the other script, which adds a value of 1 to the user inputted value, and then we print out that modified value. However, it appears you can't define a variable in one script, and have that defined variable transfer over to another script. Thus, I get this error when I try the above: NameError: name 'user_input' is not defined. I've tried to look at other posts regarding this, but they use tkinter and all are a bit too complicated/over my head to understand. So I made a very basic simple example to try and understand how this all works.
Edit:
I don't want to make another post, since its regarding the same issue. If I have to define every input used for every function, then it becomes quite crowded if you have multiple inputs. I.E.
#del2
def fun(user_input):
return int(user_input)+1
def fun2(user_input2):
return int(user_input2)+1
def fun3(user_input3):
return int(user_input3)+1
def fun4(user_input4):
return int(user_input4)+1
def fun5(user_input,user_input2,user_input3,user_input4):
return fun(user_input)+fun2(user_input2)+fun3(user_input3)+fun4(user_input4)
#script to run
user_input=input('some number')
user_input2=input('some number')
user_input3=input('some number')
user_input4=input('some number')
from del2 import fun5
print(fun5(user_input,user_input2,user_input3,user_input4))
Is there a better way to do this, so fun5 doesn't become extremely long if you have multiple inputs.
You need to define fun so it takes the variable as a parameter: def fun(user_input) then pass that variable to the imported function.
Also if you want user_inputs value to change after you call your fun() function you need to something like this:
#del2
def fun(user_input):
return int(user_input) + 1
#script to run
user_input = input('some number')
from del2 import fun
user_input = fun(user_input)
print(user_input)
Edit:
The fun() function isnt for just user_input. So you can use the same fun() function for another variables.
#del2
def fun(any_input): # i changed the variables name just to be clear
return int(any_input) + 1
#script to run
user_input = input('some number')
user_input2 = input('some number')
from del2 import fun
user_input = fun(user_input)
user_input2 = fun(user_input2)
print(user_input + ", " + user_input2)
and you can add the input variables to an array and do something like
#del2
def fun(any_input):
return int(any_input) + 2
def fun1(any_input):
return int(any_input) * 2
def fun2(any_input):
return int(any_input) // 2
def fun3(any_input):
return int(any_input) - 2
def fun5(input_array):
functions = [fun, fun1, fun2, fun3]
final = 0
if len(input_array) != 4:
raise Exception("Not enough functions for inputs")
for i in range(len(input_array)):
final += functions[i](input_array[i])
return final
#script to run
user_inputs = []
user_inputs.append(input('some number 0: ')) #you can use a for loop here too
user_inputs.append(input('some number 1: '))
user_inputs.append(input('some number 2: '))
user_inputs.append(input('some number 3: '))
from del2 import fun5
user_inputs = fun5(user_inputs)
print(user_inputs)
You can do this using the global keyword and a global variable within the imported module that is accessed by the different functions. Taking a simpler example that just adds or subtracts from a globally stored total:
# tally.py
total = 0
def add(n):
global total
total += n
def subtract(n):
global total
total -= n
# test_tally.py
import tally
tally.add(5)
tally.subtract(1)
print(tally.total)
However, global variables are bad. We do not generally think in terms of passing data back and forth between modules. The imported module is executed in its entirety at the time of importing, so data can only be passed to functions or other objects within the imported module.
Instead, modules often include classes, which can be used to generate objects that can store state. Data can be passed to these objects and stored within them. The objects can then operate upon the data and return different results by calling different methods of the object. This would be written like this:
# tally.py
class Tally(object):
def __init__(self):
self.total = 0
def add(self, n):
self.total += n
def subtract(self, n):
self.total -= n
# test_tally.py
from tally import Tally
tally = Tally()
tally.add(3)
tally.subtract(4)
print(tally.total)

Replacing global vars

Below, I came up with artificially passing the reference var n2 from f2() to g2(x) instead of the global var n in f() and the nested g(). Any other better ways to replace global vars in this case?
from random import randint
# global value var
def f():
global n
n=0
def g():
global n
if randint(1,100)>50: n+=1
for _ in range(100): g()
print(n)
# local reference var
def f2():
n2=[0]
for _ in range(100): g2(n2)
print(n2[0])
def g2(x):
if randint(1,100)>50: x[0]+=1
Short answer: You are trying to pass by reference an immutable value (integer) and want to update it. Wrapping that in a tiny class, or list, or dict like you're doing is the way to go. But there are other ways if you are able to slightly modify your code.
Longer answer: (Note: This might not be a direct answer to your question.)
I understand this is an artificial example. But think about your real problem --Does g2() need to know that there is a variable that is supposed to update as a part of its invocation? Is there a way that the responsibility of updating a variable belongs to that which defines it? How about f2() is the one that defines the variables and also updates it? That way you can limit all the changes to that variable to a very small perimeter (f2()).
My approach in that case would be something like:
def f2():
n2 = 0
for _ in range(100):
n2 += g2()
print(n2)
def g2():
return 1 if randint(1,100)>50 else 0
From working with functional languages, and from trying to write reproducible tests, I've generally tried to adopt a rule that a function should declare all of its inputs as parameters, and produce all of its outputs as return values: to the maximum extent possible a function should never have side effects. Using the global keyword probably indicates you're breaking this rule.
For example, your "g" function takes the current state variable and either increments it or doesn't. You don't need a global for that (and you don't need it to be a nested function either):
from random import randint
def g(n):
"""Returns either n or n+1, with 50% probability."""
if randint(1,100)>50:
return n+1
else:
return n
Then the iterating function can call that a bunch of times:
def f():
"""Produces a number between 0 and 100 with $DISTRIBUTION."""
n = 0
for _ in range(100):
n = g(n)
return n
And finally at the top level:
if __name__ == '__main__':
print(f())
Since we're never totally sure about our code, we can write some tests.
def test_f():
n = f()
assert n >= 0 and n < 100
def test_g():
n = g(0)
assert n == 0 or n == 1
def test_g_dist():
count = 100
ns = [g(0) for _ in range(count)]
assert(all(n == 0 or n == 1) for n in ns)
zeros = len([n for n in ns if n == 0])
ones = len([n for n in ns if n == 1])
assert zeros + ones == count
# won't always pass; probability of failure left as an exercise
assert zeros > 0.45 * count and zeros < 0.55 * count
Notice that I can call f() and g(n) as many times as I want and they'll never interfere with anything else. Running my own unit tests at startup time would be a little unusual, but I'm free to if that's what I want to do.

Variable is not defined error

I have been studying beginning game development with pygame and python, and I ran into a snag with defining functions that have arguments. The first one works, and I'm trying to make a more simple version with only one argument. It keeps saying c is not defined when it clearly is. I don't understand why. Any suggestions or ideas on this? I'm also having
def fugu_tip(price, num_plates, tip):
total = price * num_plates
tip = total * (tip / 100.)
return tip
def Character(c):
a = input("Enter a number 1 - 100")
b = input("Enter A Number 1 - 100")
c = 0
c = a + b
return c
Character(c)
I appreciate all the help guys!
This is the revised code for my project:
'$'
import pygame
import random
global Cash
global PName
global PHealth
global PHunger
global PJob
global PEmployeed
def Character1():
Cash = 0
PName = raw_input("Please Enter Name: ")
PHealth = 100
PHunger = 100
PJob = ""
PEmployeed = False
print PName, Cash, PHealth, PHunger, PJob, PEmployeed
Character1()
'$'
I'm going to rework some of the code you have rather than rewriting entirely. The thing that you are missing is scope. Inside, your function, c is defined. However, outside of your function you are trying to pass in a variable called c that is not defined.
Here's your code, fixed up.
#it's true that by convention, functions generally start with lowercase
# and Classes being with uppercase characters
def character(c = 0):
a = input("Enter a number 1 - 100")
b = input("Enter A Number 1 - 100")
return c * (a + b)
myValue = 3 #note that the variable that you pass in
# to your function does not have to have the same name as the parameter
character(myValue)
Note, I modified the behavior of the function so that i makes use of the parameter c. Now, c, the input parameter, is used to multiply the sum of the two user inputs. When I call the function the value of c becomes 3 and so what ever the user enters is added then multiplied by 3.
Also, there is a difference between def character(c): and def character(c=0):. In the first case, a value must be passed into the function when calling it. In the second case, you can skip passing in a value to the function as we have the defined the function with a default argument value. So the second function could be called directly with:
character(3)
character()
But the first could only be called correctly with
character(3)
c is defined inside your function — but not where you call Character.
You seem to set c to 0 in your function anyways — why have any parameter at all then?
Finally, you should not give your functions names that start with capital letters, as by convention that is reserved for classes.
edit:
def get_sum():
a = input("Enter a number 1 - 100")
b = input("Enter A Number 1 - 100")
c = a + b
return c
Well, the problem is c is not defined at current scope. In your case, c is only visible from inside the function Character but not from outside. So, the place from where you are calling the function has no idea what c is. As soon as you define c your code works just fine.
def Character(c):
a = input("Enter a number 1 - 100")
b = input("Enter A Number 1 - 100")
c = 0
c = a + b
return c
c = 0
Character(c)
Or maybe something like this (edit)
def Character(c):
a = input("Enter a number 1 - 100")
b = input("Enter A Number 1 - 100")
c = 0
c = a + b
return c
def call_character():
c = 0
Character(c)
call_character()
The problem is where you call Character(c). That c has not been defined. I can't suggest what you should do instead because I don't know what you are trying to do, but that is your problem. What you use instead depends on what argument you want to give to Character().
C is being passed as a argument to the function Character so it should be defined before you call then function.
You don't need to pass any argument to
Character
as it is not required for given behavior. Simply do
Character()
.
And also remove C from your function definition.
def Character():
a = input("Enter a number 1 - 100")
b = input("Enter A Number 1 - 100")
c = 0
c = a + b
return c
Character()
Edited : based on user comment

Want to refer to variable in the outer function's scope from an inner function being returned

So I'm making a simple program that gets 2 functions(a and k) and one integer value(b), then it gets the formal parameter in the two functions(a and k) which is "x" and applies a condition x < b then based on the condition makes a function call, either a or b. But when I run the program it gives an error that x is not defined in the global frame. I want it to get "x" from the formal parameter assigned to the functions a and b and then get the condition based on that.
Here's my code
def square(x):
return x * x
def increment(x):
return x + 1
def piecewise(a, k, b):
if x<b:
return a
else:
return k
mak = piecewise(increment,square ,3 )
print(mak(1))
I guess you want to do something like this:
def piecewise(a, k, b):
def f(x):
if x < b:
return a(x)
else:
return k(x)
return f
However, I am not sure if it is a good practice. So, I leave my answer here to see the comments and learn if there is any problem with it.

Python static variable in function global name not defined

I have written a function to calculate the heading between two points only if a vehicle reports that it's moving and that the vehicle has moved 20cm between points.
The function uses static variables - or at least it would if it worked - to keep track of previous positions and heading values.
Here is the code:
def withCan(pos):
eastdist = pos[0]-previous_pos[0]
northdist = pos[1]-previous_pos[1]
canflag = pos[2]
if (canflag == 1 or canflag==2):
if (previous_canflag == 1 and canflag == 2):
previous_heading += 180.0
previous_canflag = canflag
elif (previous_canflag == 2 and canflag == 1):
previous_heading += 180.0
previous_canflag = canflag
else:
previous_canflag = canflag
if ( (canflag == 1 or canflag == 2) and math.sqrt(northdist*northdist+eastdist*eastdist) > canstep ):
previous_heading = math.degrees(math.atan2(eastdist, northdist))
previous_pos[0] = pos[0]
previous_pos[1] = pos[1]
return previous_heading
withCan.previous_pos = [0.0,0.0]
withCan.previous_heading = 0.0
withCan.previous_canflag = 0
withCan.canstep = 0.2
positions = backandforth([100,100]) #populates an array of form [x,y,canflag]
for p in positions:
print withCan(p)
I am getting an error that says eastdist = pos[0]-previous_pos[0]
NameError: global name 'previous_pos' is not defined. Please could someone explain the cause of this error?
When you do this:
def foo():
pass
foo.name = 1
You are not creating a global name name. Instead you are adding a property to the foo function! You can access it with:
def foo():
return foo.name
foo.name = 1
But that is quite weird. If you need a global name, just do it:
def foo():
global name
name += 1
return name
name = 1
Remember that if you want to modify the global name from the function, you have to declare it as global. If you fail to do this, you can use it but you cannot assign to it.
Your confusion with static names may come from using classes. But note that in your code withCan is not a class, it is a plain function!
It looks like what you are trying to do is writing a class...
class WithCan():
def __init(self, previous_pos)__:
self.previous_pos=previous_pos
def withCan(self, pos):
# your function as class method
Then you could initialize an instance
withCan=WithCan(previous_pos)
and access it
withCan.previous_pos=...
You can do static variables in Python using function attributes, but you need to use the full name inside the function to access those attributes.
Here's a short demo.
def test(a):
print a, a + test.b
test.b += 1
test.b = 5
test(3)
test(10)
output
3 8
10 16
However, it would be more usual to do this sort of thing using a class, as shown in Tim's answer.
Another way to do statics in Python is to give your function default mutable arguments, but many people are uncomfortable with that. But if you're curious, please see “Least Astonishment” in Python: The Mutable Default Argument.
Let me contribute a perhaps more streamlined way of emulating static variables in functions that could make the OP's example maybe easier to read:
def with_can(pos):
if not hasattr(with_can, "canflag"):
# set up and initialise the static variables
with_can.canflag = 0
with_can.previous_pos = [0.0,0.0]
with_can.previous_heading = 0.0
with_can.canstep = 0.2
# ... use them ...
eastdist = pos[0]-with_can.previous_pos[0]
# ... etc ...
Basically at the first invocation we detect that one of the "static" variables (canflag) is not yet there so we add and initialise all of them. After that they can be used as indicated.
However, as others have pointed out already, it is much better to write a class with data members instead of these "static" function variables.

Categories