Oct-21-2020, 03:38 AM
There are a couple of errors here. First we still have the missing arguments problem. Function calcIncome takes three arguments: aTickets, bTickets and cTickets. When you call calcIncome(aRevenue) there is only 1 value for 3 positional arguments. We will fix this problem first.
One way to fix the problem and make this function more useful is get rid of the positional arguments. This is the same function, but it uses named arguments.
There is a reason these are called "named arguments", and that is because we can use the argument name to specify which argument gets the value. If I wanted individual tallies for a, b and c tickets I was forced to do this when calcIncome used position arguments
That fixes the position argument problem and makes your function more useful. Now time to fix the "aRevenue not defined" problem.
"Where oh where is aRevenue defined? Oh where oh where can that be?" That is what the message says. Python is looking for something named "aRevenue" and it cannot find it. First Python looks in the function where aRevenu is used.
But Python doesn't just give up. Next it looks for "aRevenue" in the module. A module in Python is just a file, so Python looks to see if "aRevenue" is defined in the file. You may look at the file and see "aRevenue" appears in the function calcIncome().
To make it possible to write Python code and to allow for collaboration, Python breaks up all of Python into things called "name spaces". Every file of Python code defines its own namespace. If I write a function and you write a program that uses my function you don't have to worry about what variable names I used. These namespaces are why you have to write things like:
Modules aren't the only namespace. In Python each function get's its own namespace. This lets you reuse variable names in functions that reside in the same module. Some Python modules are pretty big and I don't want to read all the code to see if I can use a variable named "i" in the function I am adding to the module. This function namespace is why "showIncome()" cannot see "aRevenue" inside the "calcIncome()" function. "aRevenue" is inside the "calcIncome()" namespace.
You might be tempted to get the variable using the namespace.attribute notation; calcIncome.aRevenue. This does not work. The reason this does not work is because the function namespace calcIncome() only exists while the code inside calcIncome() is executing. As soon as the function completes the variables local to the function are deleted and their memory reclaimed.
Knowing all about namespaces and named arguments it is clear how you should write this program.
One way to fix the problem and make this function more useful is get rid of the positional arguments. This is the same function, but it uses named arguments.
A_COST = 10 B_COST = 7.50 C_COST = 2.25 def calcIncome(aTickets=0, bTickets=0, cTickets=0): # revenue made from each and returns total revenue from sales return aTickets * A_COST + bTickets * B_COST + cTickets * C_COST print('A Ticket Income', calcIncome(aTickets = 5)) print('B Ticket Income', calcIncome(bTickets = 5)) print('C Ticket Income', calcIncome(cTickets = 5))
Output:A Ticket Income 50.0
B Ticket Income 37.5
C Ticket Income 11.25
A named argument has a default value. If I call calcIncome(5), the value 5 is used for the aTickets argument leaving no values for the b and c tickets arguments. However the function declaration says it is ok to not provide values for every argument and the default value will be used when no value is provided. Calling calcIncome(5) is the same as calling calcIncome(5, 0, 0)There is a reason these are called "named arguments", and that is because we can use the argument name to specify which argument gets the value. If I wanted individual tallies for a, b and c tickets I was forced to do this when calcIncome used position arguments
print('A Ticket Income', calcIncome(5, 0, 0)) print('B Ticket Income', calcIncome(0, 5, 0)) print('C Ticket Income', calcIncome(0, 0, 5))This works ok, but it is not as clear as the code above that uses a named argument. When I call calcIncome(aTickets=5) it is immediately clear to the reader that the number of aTickets is 5. The reader does not have to search for the function and look at the order of arguments,
That fixes the position argument problem and makes your function more useful. Now time to fix the "aRevenue not defined" problem.
"Where oh where is aRevenue defined? Oh where oh where can that be?" That is what the message says. Python is looking for something named "aRevenue" and it cannot find it. First Python looks in the function where aRevenu is used.
def showIncome(num_aTickets, num_bTickets, num_cTickets, aRevenue, bRevenue, cRevenue): income = calcIncome(num_aTickets, num_bTickets, num_cTickets) print('the revenue collected from total ticket sales is', income) print('the revenue collected from class a ticket sales is', aRevenue) print('the revenue collected from class b ticket sales is', bRevenue) print('the revenue collected from class c ticket sales is', cRevenueIn Python, variables are defined when you set their value. Is there anywhere in this function where you see "aRevenue ="? You don't see it, I don't see it, and Python doesn't see it.
But Python doesn't just give up. Next it looks for "aRevenue" in the module. A module in Python is just a file, so Python looks to see if "aRevenue" is defined in the file. You may look at the file and see "aRevenue" appears in the function calcIncome().
def calcIncome(aTickets, bTickets, cTickets): # revenue made from each and returns total revenue from sales aCost = 20 bCost = 15 cCost = 10 aRevenue = aTickets * aCost bRevenue = bTickets * bCost cRevenue = cTickets * cCost return (aRevenue + bRevenue + cRevenue)While it is true that the name "aRevenue" is used for a variable in calcIncome(), that variable is local to calcIncome() and cannot be used outside the function calcIncome(). There are many reasons for this, but the most important is that if Python did not work this way every variable used in every function would have to be unique. Not only that, but every function name would have to be unique. Any time you wrote any Python code you would have to search through every bit of code in your Python distribution to verify that any variable or function name you wanted to use had never been used before.
To make it possible to write Python code and to allow for collaboration, Python breaks up all of Python into things called "name spaces". Every file of Python code defines its own namespace. If I write a function and you write a program that uses my function you don't have to worry about what variable names I used. These namespaces are why you have to write things like:
import math sqrt2 = math.sqrt(2)The math module defines a namespace named "math". The "math" namespace contains a function named "sqrt". If I want to use the function I specify the namespace so Python knows where to look for the function.
Modules aren't the only namespace. In Python each function get's its own namespace. This lets you reuse variable names in functions that reside in the same module. Some Python modules are pretty big and I don't want to read all the code to see if I can use a variable named "i" in the function I am adding to the module. This function namespace is why "showIncome()" cannot see "aRevenue" inside the "calcIncome()" function. "aRevenue" is inside the "calcIncome()" namespace.
You might be tempted to get the variable using the namespace.attribute notation; calcIncome.aRevenue. This does not work. The reason this does not work is because the function namespace calcIncome() only exists while the code inside calcIncome() is executing. As soon as the function completes the variables local to the function are deleted and their memory reclaimed.
Knowing all about namespaces and named arguments it is clear how you should write this program.
A_COST = 10 ## These were not defined either B_COST = 7.50 C_COST = 2.25 def calcIncome(aTickets=0, bTickets=0, cTickets=0): # revenue made from each and returns total revenue from sales return aTickets * A_COST + bTickets * B_COST + cTickets * C_COST def showIncome(aTickets, bTickets, cTickets): income = calcIncome(aTickets, bTickets, cTickets) print('Total ticket sales is', income) print('Class a ticket sales is', calcIncome(aTickets=aTickets)) print('Class b ticket sales is', calcIncome(bTickets=bTickets)) print('Class c ticket sales is', calcIncome(cTickets=cTickets)) showIncome(10, 10, 10)
Output:Total ticket sales is 197.5
Class a ticket sales is 100.0
Class b ticket sales is 75.0
Class c ticket sales is 22.5
There are a few tricky things going on here like: calcIncome(aTickets=aTickets). This is less confusing if written as calcIncome(arg=value), but the two are very similar. In "calcIncome(aTickets=...)", the "aTickets is a named argument of the function "calcIncome()". The "aTickets" in calcIncome(...=aTickets) is the value of the "aTickets" argument of the "showIncome()" function. The code above is identical to this code that uses different names for the showIncome() function arguments.def showIncome(a, b, c): income = calcIncome(a, b, c) print('Total ticket sales is', income) print('Class a ticket sales is', calcIncome(aTickets=a)) print('Class b ticket sales is', calcIncome(bTickets=b)) print('Class c ticket sales is', calcIncome(cTickets=c))