Do you change variables AFTER you run a function in python? - python

So I wrote this function from a book I am reading, and this is how it starts:
def cheese_and_crackers(cheese_count, boxes_of_crackers):
print "You have %d cheeses!" % cheese_count
print "You have %d boxes of crackers!" % boxes_of_crackers
print "Man that's enough for a party!"
print "Get a blanket.\n"
ok, makes sense. and then, this is when this function is run where I got a little confused and wanted to confirm something:
print "OR, we can use variables from our script:"
amount_of_cheese = 10
amount_of_crackers = 50
cheese_and_crackers(amount_of_cheese, amount_of_crackers)
the thing that confused me here is that the amount_of_cheese and amount_of_crackers is changing the variables (verbage? not sure if i am saying the right lingo) from cheese_count and boxes_of_crackers repectively from the first inital variable labels in the function.
so my question is, when you are using a different variable from the one that is used in the initial function you wrote, why would you change the name of the AFTER you wrote out the new variable names? how would the program know what the new variables are if it is shown after it?
i thought python reads programs top to bottom, or does it do it bottom to top?
does that make sense? i'm not sure how to explain it. thank you for any help. :)
(python 2.7)

I think you are just a bit confused on the naming rules for parameter passing.
Consider:
def foo(a, b):
print a
print b
and you can call foo as follows:
x = 1
y = 2
foo(x, y)
and you'll see:
1
2
The variable names of the arguments (a, b) in the function signature (1st line of function definition) do not have to agree with the actual variable names used when you invoke the function.
Think of it as this, when you call:
foo(x, y)
It's saying: "invoke the function foo; pass x in as a, pass y in as b". Furthermore, the arguments here are passed in as copies, so if you were to modify them inside the function, it won't change the values outside of the function, from where it was invoked. Consider the following:
def bar(a, b):
a = a + 1
b = b + 2
print a
x = 0
y = 0
bar(x, y)
print x
print y
and you'll see:
1
2
0
0

The script runs from top to bottom. The function executes when you call it, not when you define it.
I'd suggest trying to understand concepts like variables and function argument passing first.

def change(variable):
print variable
var1 = 1
change(var1)
In the above example, var1 is a variable in the main thread of execution.
When you call a function like change(), the scope changes. Variables you declared outside that function cease to exist so long as you're still in the function's scope. However, if you pass it an argument, such as var1, then you can use that value inside your function, by the name you give it in the function declaration: in this case, variable. But it is entirely separate from var! The value is the same, but it is a different variable!

Your question relates to function parameter transfer.
There are two types of parameter transfer into a function:
By value ------- value changed in function domain but not global domain
By reference ------- value changed in global domain
In python, non-atomic types are transferred by reference; atomic types (like string, integer) is transferred by value.
For example,
Case 1:
x = 20
def foo(x):
x+=10
foo()
print x // 20, rather than 30
Case 2:
d = {}
def foo(x): x['key']=20
foo(d)
print d // {'key': 20}

Related

How to print the values of parameters passed into a function

I don’t know how to retrieve, store and print the values of parameters passed into a function. I do know that many posts are related to this question, but I couldn't find anything that matches the simple thing I would like to do.
Let’s take a very simple example:
def times(value, power):
return value**power
If I run this function and then write:
x = times(2.72, 3.1)
print(f'Result of calculation is: {x: .6f}')
then the output will be:
Result of calculation is: 22.241476
OK, but this is not what I would like to have; I would like to be able to print the result, the value and the power, and have the following lines as output, preferably using a print as above; something like print(f’some text here: {something}’)…
Desired output:
Result of calculation is: 22.241476
Value passed to function was: 2
Power passed to function was: 3
What is the most effective way to do that?
The question appears to be asking about accessing the function's namespace, not just printing the value of the variables. If the namespace concept is new to you, I recommend reading the Python documentation and Real Python's blog post on Namespace's in Python. Let's look at a few ways to do what you are asking.
Printing the values is straightforward:
def times(value, power):
print(f"Value passed to function was: {value}")
print(f"Power passed to function was: {power}")
print(f'Result of calculation is: {x: .6f}')
If you need to print it out the way you describe in your question, the values should be returned. This can be accomplished by updating your function to:
def times(value, power):
return value, power, value**power
v, p, result = times(2,3)
print(f'Result of calculation is: {result: .6f}')
print(f"Value passed to function was: {v}")
print(f"Power passed to function was: {p}")
However, returning parameters seems a little odd since one would assume you as the developer can capture those values elsewhere in your code. If you want to view the variables and their values for a given namespace, use the corresponding function. For viewing the value and power variables, which live in the function times() local namespace, use locals() which returns a dictionary object that is a copy of the current local namespace.
def times(value, power):
print(locals())
return value**power
>>> times(5, 4)
{'value': 5, 'power': 4}
625
If the variables are defined in the global namespace, (keep in mind global variables should be used with care) you can use globals() to look up the value in the global namespace:
VALUE = 2
POWER = 3
def times(value=VALUE, power=POWER):
return value**power
>>> globals()['VALUE']
2
>>> globals()['POWER']
3
I hope this helps you figure out how to accomplish what you are working on. I recommend taking some time to read about how Python views and manages namespaces. If you want to watch a video, check out this PyCon talk by Raymond Hettinger on object oriented programming 4 different ways.
You will need to first store the parameters in variables in the code that calls the function.
Assuming the function 'times' is defined.
a = 2.72
b = 3.1
x = times(a, b)
print(f'Result of calculation is: {x: .6f}')
print(f'Value passed to function was: {a}')
print(f'Power passed to function was: {b}')
You can always just add more "print" lines.
So the code would look something like this:
def times(value, power):
print(f'Result of calculation is: {x: .6f}')
print(f'Value passed to function was: {value}')
print(f'Power passed to function was: {power}')
and then you can just pass the values into the function like so:
times(2.72, 3.1)
Please try the following code. It uses the concept of closure (google it). Hope it is helpful.
def times():
value = float(input('Enter a value:'))
power = float(input('Enter a power: '))
def raise_to_power():
return value ** power
print(
f'Result of calculation is: {raise_to_power(): .6f}\nValue passed to function was: {value}\nPower passed to function was: {power}')
times()

Python: Keep changes on a variable made within a function

I have a question on a fairly simple task in python, however, I didn't manage to find a solution. I would like to assign a new value to an already existing variable in python. However, the changes I am doing to the variable within the function don't stick to the variable.
Here is a simplified example of my problem:
y = 1
x = None
def test(var):
var = y
return var
test(x)
print(x)
The print simply returns none. So the changes I have done to the variable within the function are non permanent.
How can I make the changes on the input-variable of the function permanent?
Thanks in advance!
Variables in Python are just names which refer to objects. In an expression, the name is a stand-in for the actual object. Saying test(x) means "pass the object referred to by x into test". It does not mean "pass the symbol x into test".
In addition, re-assigning a name only changes what object that name refers to. It affects neither the object nor any of its aliases.
In short, the name var you modify inside test has no relation to x at all.
The preferred way to have a function change something is by reassigning the result:
x = 2
def change(var):
return var * 2
x = change(x) # x now refers to 4 instead of 2
print(x)
If you want to change a name outside a function, you can use the nonlocal and global keywords:
x = 2
def change_x():
global x
x = x * 2
change_x() # x now refers to 4 instead of 2
print(x)
While this can make some trivial problems easy to solve, it is generally a bad idea for larger programs. Using global variables means one can no longer use the function in isolation; results may depend on how often and in what order such a function is called.
If you have some self-contained group of values and means to modify them, a class can be used to describe this:
class XY:
def __init__(self, x, y):
self.x, self.y = x, y
def swap(self):
self.x, self.y = self.y, self.x
my_values = XY(None, 1)
print(my_values.x, my_values.y)
my_values.swap()
print(my_values.x, my_values.y)
In contrast to global variables, you can create as many isolated instances of classes as needed. Each instance can be worked on in isolation, without affecting the others.
You can also use mutable values to make changes visible to the outside. Instead of changing the name, you modify the value.
x = [2] # x is a mutable list, containing the value 2 at the moment
def change(var):
var[0] = 4 # change leading element of argument
change(x) # x now contains 4 instead of 2
print(x)
This is an example of passing variables to functions by value. By default, when you pass a variable to a function in Python, it is passed by value.
What it means is, that a new variable with a new scope is created with the same value of x. So, any change that happens to the new x is not reflected to the x outside the function's scope.
If you want to get the value from the function back, you can use the return statement (as you have used). return returns the value back from the function. However, in your example there is no variable to receive it. Hence, it is lost.
You would have to call the function as x = test(x). This ensures that x receives the value back from the function.

Why does the scope work like this?

This question is a result of a student of mine asking a question about the following code, and I'm honestly completely stumped. Any help would be appreciated.
When I run this code:
#test 2
a = 1
def func2(x):
x = x + a
return(x)
print(func2(3))
it works perfectly fine. It is able to take the globally-scoped variable a and use its value to perform the calculation and return the value 4.
However, if I change it to this:
# test 3
a = 1
def func3(x):
a = x + a
return(x)
print(func3(3))
I then get an error:
local variable 'a' referenced before assignment
Why do I get this error only when I want to update the value of a within the function to a new value based on its original value? What am I not understanding? I feel like this second piece of code should work fine.
Thanks in advance for any help and insight.
a = 1
def func3(x):
global a
a = x + a
return(x)
print(func3(3))
Now it should work.
When you put the statement a=x+a inside the function, it creates a new local variable a and tries to reference its value(which clearly hasnt been defined before). Thus you have to use global a before altering the value of a global variable so that it knows which value to refer to.
EDIT:
The execution of a function introduces a new symbol table used for the
local variables of the function. More precisely, all variable
assignments in a function store the value in the local symbol table;
whereas variable references first look in the local symbol table, then
in the local symbol tables of enclosing functions, then in the global
symbol table, and finally in the table of built-in names. Thus, global
variables cannot be directly assigned a value within a function
(unless named in a global statement), although they may be referenced.
def func3(x):
a = x + a
return(x)
On the right hand right side of a = x + a (So, x + a), 'x' is passed as variable, where 'a' is not passed as a variable thus an error.
Without using globals:
a = 1
def func3(x, a=2):
a = x + a
return(x)
func3(3)
Returns: 5

Method Overriding?

I saw this particular piece of code:
def g(x,y):
return x+y
def g(x,y):
return x*y
x,y=6,7
print (g(x,y))
The output is obviously(but not to me) is 42. Can somebody please explain this behavior? This is method overriding I suppose, but I'm still not getting the flow here.
When you define a function, and you redefine it, it will use the last one you defined, even the parameter is different:
def g(x,y):
return x+y
def g(x,y):
return x*y
x,y=6,7
print (g(x,y))
def hello():
return 'hello'
def hello():
return 'bye'
print hello()
def withone(word):
return word
def withone():
return 1==1
print withone('ok')
Output:
42
bye
TypeError: withone() takes no arguments (1 given)
And function name in Python is more like simple variable:
def hello():
return 'hello'
iamhello = hello # bind to the old one
def hello():
return 'bye'
print hello() # here is the new guy
print iamhello()
OutPut:
bye
hello
The devil is in the order of function definitions.
This is not technically method overriding as that requires class inheritance, instead it's a result of how python declares and references functions.
When declaring a function, python stores a reference to that function in a variable named after the function definition. (e.g. variable would be "foo" for "def foo():")
By declaring the function twice, the value of that variable gets overwritten by the second definition.
A Python script is parsed from top till bottom.
So anytime the same name of a variable or function or class occurs, it overwrites any definitions that where associated with this name before.
def g(x,z):
print('first')
def g():
print('second')
g = 3
print g
print g()
So look at this example which will result in the printout of '3' and then in an Exception: 'TypeError: 'int' object is not callable'
The name g is at first a function with two parameters, then it gets redefined to be a function with no parameters, then it gets redefined to be an int.
Which cannot be called obviously :)
Everything in python is treated as object, whether it be a function name or class name. So, when we define a function using 'def', the memory allocation is done for that method. Then python points the name that we assign to the function, to this allocated memory location. So if we define a method :-
def demo():
print 'hi'
the memory is allocated for the method, and the name 'demo' is pointed to its memory location as follows :-
Now as described by zoosuck in his second example, when you assign the function name to another variable :-
demo2 = demo # bind to the old one
then in that case, the assigned memory location to demo, is assigned to demo2 as well. So both demo and demo2 points to same location 12506.
print id(demo) # will print 12506
print id(demo2) # will print 12506
Now if we modify the above piece of code and in the next line, define a new method with same name demo:-
def demo():
print 'hi'
demo2 = demo # bind to the old one
demo() # Will print hi
def demo():
print "hello"
demo() # Will print hello
demo2() # Will print hi
then a completely new memory location 12534 is allocated for this new method, and now demo will point to this new location 12534 instead of pointing to the old one i.e. to 12506. But demo2 is still pointing to the location 12506.
I hope this will give you a clear idea of what is going on and how the method name is over-written.
Order matters, if names are same,last function you defined is processing. In your case it's;
def g(x,y):
return x*y
g is just a variable. The fact that the object it refers to is a function doesn't make it special in Python, so you can assign and reassign it as you want. In this case, the second assignment (which is what a function definition is) simply replaces the object stored there with a different one.
Functions and methods are normal objects like any others. So in
def g(x, y):
return x + y
def g(x, y):
return x * y
the second object g will override(replace) the first one, just like object a does below:
a = 1
a = 2
The number, type or order of parameters does not make any difference, because Python does not support function/method override and does not allow two functions/methods to have the same name.
If you are familiar with lambda function, also often called anonymous\inline functions, this might clear things up a bit
These two code blocks are essentially equal
def g(x,y):
return x+y
def g(x,y):
return x*y
g = lambda x,y: x+y
g = lambda x,y: x*y

Returning a counter outside of a Python Function

I am trying to build some code and I have defined a function as this to test how a counter works inside of the function:
def errorPrinting(x):
x += 1
return x
I then use the function in some conditional logic where I want the counter to increase if the conditions are met.
x = 1
for row in arcpy.SearchCursor(fc):
if not row.INCLUSION_TYPE or len(row.TYPE.strip()) == 0:
errorPrinting(x)
print x
elif len(row.TYPE) not in range(2,5):
errorPrinting(x)
print x
elif row.INCLUSION_TYPE.upper() not in [y.upper() for y in TableList]:
errorPrinting(x)
print x
I'm still fairly new with using functions, so maybe I am not understanding how to return the value back outside of the function to be used in the next iteration of the for loop. It keeps returning 1 on me. Can anyone show me how to return the x outside of the function after it has been increased by one x+= 1?
Thanks,
Mike
You're not incrementing your global x, you're incrementing the local paramater that also happens to be named x! (Your parameter to errorPrinting could have been named anything. I'm calling it xLocal.)
As you can see here, x isn't incremented by the function.
>>> def inc(xLocal):
... xLocal += 1
... return xLocal
...
>>> x = 4
>>> inc(x)
5
>>> x
4
You need to reassign the value of x to the return value of the function each time. Like this
x = 1
for row in arcpy.SearchCursor(fc):
if not row.INCLUSION_TYPE or len(row.TYPE.strip()) == 0:
x = errorPrinting(x) # <=== here
print x
elif len(row.TYPE) not in range(2,5):
x = errorPrinting(x) # <=== here
print x
elif row.INCLUSION_TYPE.upper() not in [y.upper() for y in TableList]:
x = errorPrinting(x) # <=== here
print x
Integral parameters and other primitives aren't normally passed by reference in Python. (Lists, dicts, etc. are. Modifying lists unintentionally is actually a very common mistake in Python.)
Edit: passing by "reference" and "value" isn't really correct to talk about in Python. See this nice question for more details.
So, using my previous example:
>>> x = 4
>>> x = inc(x)
>>> x
5
Note that if this had been parameter that is passed by reference, like a list, this strategy would have worked.
>>> def incList(xList):
... for i in range(len(xList)):
... xList[i] += 1
...
>>> xList
[1]
>>> incList(xList)
>>> xList
[2]
Note that normal, Pythonic syntax:
for i in xList:
i += 1
would not increment the global value.
Note: If you're looking to keep tabs on a lot of things, I also recommend the logging module that #SB. mentioned. It's super useful and makes debugging large programs a lot easier. You can get time, type of message, etc.
You're bit by scope. You may want to check out this link for a quick primer.
You can do something simple and say x = errorPrinting(x) in all cases you call errorPrinting and get what you want. But I think there are better solutions where you'll learn more.
Consider implementing an error logger object that maintains a count for you. Then you can do logger.errorPrinting() and your instance of logger will manage the counter. You may also want to look into python's built in logging facilities.
Edited for the OP's benefit, since if functions are a new concept, my earlier comments may be a little hard to follow.
I personally think the nicest way to address this issue is to wrap your related code in an object.
Python is heavily based on the concept of objects, which you can think of as grouping data with functions that operate on that data. An object might represent a thing, or in some cases might just be a convenient way to let a few related functions share some data.
Objects are defined as "classes," which define the type of the object, and then you make "instances," each of which are a separate copy of the grouping of data defined in the class.
class MyPrint(object):
def __init__(self):
self.x = 1
def errorPrinting(self):
self.x += 1
return self.x
def myPrint(self):
for row in arcpy.SearchCursor(fc):
if not row.INCLUSION_TYPE or len(row.TYPE.strip()) == 0:
self.errorPrinting()
print self.x
elif len(row.TYPE) not in range(2,5):
self.errorPrinting()
print self.x
elif row.INCLUSION_TYPE.upper() not in [y.upper() for y in TableList]:
self.errorPrinting()
print self.x
p = MyPrint()
p.myPrint()
The functions __init__(self), errorPrinting(self), and myPrint(self), are all called "methods," and they're the operations defined for any object in the class. Calling those functions for one of the class's instance objects automatically sticks a self argument in front of any arguments that contains a reference to the particular instance that the function is called for. self.x refers to a variable that's stored by that instance object, so the functions can share that variable.
What looks like a function call to the class's name:
p = MyPrint()
actually makes a new instance object of class MyPrint, calls MyPrint.__init__(<instance>), where <instance> is the new object, and then assigns the instance to p. Then, calling
p.myprint()
calls MyPrint.myprint(p).
This has a few benefits, in that variables you use this way only last as long as the object is needed, you can have multiple counters for different tasks that are doing the same thing, and scope is all taken care of, plus you're not cluttering up the global namespace or having to pass the value around between your functions.
The simplest fix, though perhaps not the best style:
def errorPrinting():
global x
x += 1
Then convert x=errorPrinting(x) to errorPrinting ()
"global x" makes the function use the x defined globally instead of creating one in the scope of the function.
The other examples are good though. Study all of them.

Categories

Resources