Python: Keep changes on a variable made within a function - python

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.

Related

Use a dictionary to refer to a global variable inside a function (Python)

I'm using a dictionary to refer to a global variable in Python inside a function. I want to use the function to update the global variable. (In reality my global variables are more complicated than this.)
global_variable_A=5
global_variable_B=1
dictionary={'A':global_variable_A,'B':global_variable_B}
def f(x,glob):
global global_variable_A
dictionary[glob]+=x
f(2,'A')
print(global_variable_A)
This returns 5 rather than 7. I understand why, is there anyway to let Python know that I mean the variable dictionary[glob] to refer to the global rather than the local variable, while still referring to the variable through a dictionary?
Thanks for your time, and apologies if I've missed something completely obvious.
When you assign a value to a name name = 5, you're creating a reference to 5 that you can use the identifier name to access. Normally, if you then have some code with a narrower scope, you can either use that reference
def f():
print(name)
or create a local reference using the same identifier, potentially to an unrelated value
def g():
name = 100
print(name)
The global keyword allows you to instead manipulate the identifier as if you weren;t in the more narrow scope, allowing you to reassign the global name to a different reference:
def h():
global name
name = 100
h()
print(name) # 100
However, when you use a reference to create another reference, there isn't any relation ship between those two references. So
name = 5
l = [name]
leaves us with two references to the value 5: one from the identifier name, and one from the first position of l. Crucially, those two references are not related; we can change one without changing the other.
name = 6
print(l) # [5]
One way to accomplish what you want is to use a boxed type. That means that you create an object that points to another object. All of your references can then point to the first object (the box) and you can freely change what it points to (the thing "inside" the box), by only updating a single reference:
class Box:
def __init__(self, value):
self.value = value
box = Box(5)
l = [box]
box.value = 10
print(l[0].value) # 10
If you think you need to do this, there's a design flaw in your code. I really wouldn't recommend doing this.
global_a = 5
def add_to_global(key, value):
globals()[key] += value
add_to_global("global_a", 2)
print(global_a)
Output:
7

Difference between Class variables and Instance variables

I have already read many answers here on Stack Exchange like Python - why use "self" in a class?
After reading these answers, I understand that instance variables are unique to each instance of the class while class variables are shared across all instances.
While playing around, I found that this code which gives the output [1]:
class A:
x = []
def add(self):
self.x.append(1)
x = A()
y = A()
x.add()
print "Y's x: ", y.x
However, this code gives 10 as the output, when in my opinion it should be 11:
class A:
x = 10
def add(self):
self.x += 1
x = A()
y = A()
x.add()
print "Y's x: ", y.x
Why A class variable is not updated when I run x.add()? I am not very experienced in programming, so please excuse me.
Class variables are shadowed by instance attribute. This means that when looking up an attribute, Python first looks in the instance, then in the class. Furthermore, setting a variable on an object (e.g. self) always creates an instance variable - it never changes the class variable.
This means that when, in your second example you do:
self.x += 1
which is (in this case, see footnote) equivalent to:
self.x = self.x + 1
what Python does is:
Look up self.x. At that point, self doesn't have the instance attribute x, so the class attribute A.x is found, with the value 10.
The RHS is evaluated, giving the result 11.
This result is assigned to a new instance attribute x of self.
So below that, when you look up x.x, you get this new instance attribute that was created in add(). When looking up y.x, you still get the class attribute. To change the class attribute, you'd have to use A.x += 1 explicitly – the lookup only happens when reading the value of an attribute.
Your first example is a classical gotcha and the reason you shouldn't use class attributes as "default" values for instance attributes. When you call:
self.x.append(1)
there is no assignment to self.x taking place. (Changing the contents of a mutable object, like a list, is not the same as assignment.) Thus, no new instance attribute is added to x that would shadow it, and looking up x.x and y.x later on gives you the same list from the class attribute.
Note: In Python, x += y is not always equivalent to x = x + y. Python allows you to override the in-place operators separately from the normal ones for a type. This mostly makes sense for mutable objects, where the in-place version will directly change the contents without a reassignment of the LHS of the expression. However, immutable objects (such as numbers in your second example) do not override in-place operators. In that case, the statement does get evaluated as a regular addition and a reassignment, explaining the behaviour you see.
(I lifted the above from this SO answer, see there for more details.)

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.

Do you change variables AFTER you run a function in 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}

python binding of variable to particular expression

I am developing a simple application which hava a file Constants.py containing all configuration, it is like this
x = y
during execution of program , the value of y changes , I want value of x o get updated too , automatically, this can be reffered as binding, how can I achieve this
In Python variable names point at values. x=y tells Python that the variable name x should point at the value that y is currently pointing at.
When you change y, then the variable name y points at a new value, while the variable name x still points at the old value.
You can not achieve what you want with plain variable names.
I like KennyTM's suggestion to define x as a function since it makes explicit that the value of x requires running some code (the lookup of the value of y).
However, if you want to maintain a uniform syntax (making all the constants accessible in the same way), then you could use a class with properties (attributes which call getter and setter functions):
Constants.py:
class BunchOConstants(object):
def __init__(self, **kwds):
self.__dict__.update(kwds)
#property
def x(self):
return self.y
#x.setter
def x(self,val):
self.y=val
const=BunchOConstants(y=10,z='foo')
Your script.py:
import Constants
const=Constants.const
print(const.y)
# 10
print(const.x)
# 10
Here you change the "constant" y:
const.y='bar'
And the "constant" x is changed too:
print(const.x)
# bar
You can change x also,
const.x='foo'
and y too gets changed:
print(const.y)
# foo
If you change the value (object) itself, then all references to it will be updated:
>>> a = []
>>> b = a # b refers to the same object a is refering right now
>>> a.append('foo')
>>> print b
['foo']
However, if you make the name point to some other object, then other names will still reference whatever they were referencing before:
>>> a = 15
>>> print b
['foo']
That's how python works. Names are just references to objects. You can make a name reference the same object another name is referencing, but you can't make a name reference another name. Name attribution using the = operator (a = 15) changes what a refers to, so it can't affect other names.
if your configuration values are inside a class, you could do something like this:
>>> class A(object):
... a = 4
... #property
... def b(self):
... return self.a
...
then, every time you access b, it will return the value of a.
There is a simple solution you can do. Just define a property and ask for the fget value you defined.
For example:
a = 7
#property
def b():
return a
if you ask for b, you will get something like this <property object at 0x1150418> but if you do b.fget(), you will obtain the value 7
Now try this:
a = 9
b.fget() # this will give you 9. The current value of a
You don't need to have a class with this way, otherwise, I think you will need it.

Categories

Resources