Function within a function to alter a variable - python

This is a simplified section of code from a game I'm trying to make. There is a condition in primary_function that if satisfied will run the action, in this case secondary_function fed into it, action itself being a function which alters a particular variable. Problem is that it doesn't alter the variable. Print(variable) returns 1.
variable = 1
def secondary_function(var):
var += 1
def function(parameter_1, parameter_2, action = None, var = None):
if condition == 'satisfied':
action(var)
function(1, 2, secondary_function, variable)
print(variable)

Variables in Python are not passed by reference, and there is no way to do so. So a function that takes an integer argument and just increments it can never impact the original variable. If you want to modify the original variable, you will have to assign it a new value instead.
def secondary_function(var):
return var + 1
variable = 1
variable = secondary_function(variable) # variable is now 2
Similary, you will have to modify your function function too to return the value instead:
def function (parameter_1, parameter_2, action = None, var = None):
if parameter_1 == 1:
return action(var)
variable = function(1, 2, secondary_function, variable)
Python has no notion of pass-by-reference or pass-by-value. Instead, every variable is passed by assignment. That means that whenever you call a function and pass a variable, the behavior is the same as if you just did function_argument = variable; you are assigning the value of the source variable to the function argument. Since Python is object oriented, this effectively means that you copy the reference to the object that variable refers to. As such, when the object is mutable, you can modify it and impact other variables referencing the same object. But for immutable objects (ints, strings, tuples, etc.), you cannot modify the existing object, so when you “modify” it, you always assign a new object to the variable, without affecting all the other variables that still point to the old object.

Function arguments in Python as passed by value. Thus, function(var) sees a copy of variable in var and secondary_function(var) sees yet another copy.

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

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.

Python 3 parameter closed upon by inner/nested method falls out of scope and triggers UnboundLocalError

Edit: Why are people downvoting this post? Are Python developers really this inept? It's a legitimate question, not one that's been answered in other places. I searched for a solution. I'm not an idiot. One parameter has a value and the other one is undefined, but if you actually read the post, you will see that both of them appear to be equally scoped.
First of all, I assure you that this question is unlike other questions involving the error message:
UnboundLocalError: local variable referenced before assignment closure method
As I'm looking at this code, it appears that the parameter, uuidString, of the top-level method, getStockDataSaverFactory, should actually be in-scope when the method returns its inner method, saveData, as a first-class function object... because to my amazement, the parameter tickerName IS in-scope and does have the value 'GOOG' when the saveData() method is called (e.g. by the test method testDataProcessing_getSaverMethodFactory), so we can actually see that it has an actual value when the method, getDataMethodFactory(..)
is called, unlike uuidString.
To make the matter more obvious, I added the lines:
localUuidString = uuidString
and
experimentUuidString = localUuidString
to show that the parameter uuidString has an available value when the method is inspected by a breakpoint.
def getStockDataSaverFactory(self, tickerName, uuidString, methodToGetData, columnList):
# This method expects that methodToGetData returns a pandas dataframe, such as the method returned by: self.getDataFactory(..)
localUuidString = uuidString
def saveData():
(data, meta_data) = methodToGetData()
experimentUuidString = localUuidString
methodToNameFile = self.getDataMethodFactory(tickerName, uuidString)
(full_filepathname, full_filename, uuidString) = methodToNameFile()
methodToSaveData = self.getDataFrameSaverFactory(methodToGetData, columnList, full_filepathname)
# We might want try/catch here:
methodToSaveData()
# A parameterless method that has immutable state (from a closure) is often easier to design around than one that expects parameters when we want to pass it with a list of similar methods
return (full_filepathname, full_filename, uuidString)
return saveData
def testDataProcessing_getSaverMethodFactory(self):
dataProcessing = DataProcessing()
getSymbols = dataProcessing.getSymbolFactory(
dataProcessing.getNasdaqSymbols(dataProcessing.getListOfNASDAQStockTickers))
tickers = getSymbols()
uuidString = 'FAKEUUID'
columnList = ['low', 'high']
tickerSubset = tickers[0:2]
methodsToPullData = map(lambda ticker: dataProcessing.getStockDataSaverFactory(ticker,
uuidString,
dataProcessing.getDataFactory(
ticker),
columnList), tickerSubset)
savedPathTuples = [f() for f in methodsToPullData]
savedFileNames = [pathTuple[0] for pathTuple in savedPathTuples]
for fileName in savedFileNames:
self.assertTrue(os.path.isfile(fileName))
os.remove(fileName)
Just to make it clear that uuidString has no value but ticker does have a value, I'm including this screenshot:
Notice that in the variable watch window, uuidString is undefined, but ticker has the string value of "A".
Is there something unique about Python (or Python 3) that is resulting in this behavior?
The problem is that you reference uuidString in the call to self.getMethodThatProvidesFullFilePathNameForPricesCsvFromUUIDAndTickerName before you assign to it. The assignment makes it local to the scope of the innermost function and therefore, it is unassigned when you reference it.
A full description of the scoping rules is provided by: https://stackoverflow.com/a/292502/7517724
This simpler example reproduces your error to make the problem more clear:
class aclass():
def outer(self, uuidString):
def inner():
print(uuidString)
uuidString = 'new value'
return uuidString
return inner
a = aclass()
func = a.outer('a uuid')
val = func()
print(val)
The assignment in inner() causes the uuidString to be local to inner() and therefore it is unassigned when the print(uuidString) is call, which causes Python to raise the UnboundLocalError.
You can fix the error by passing the variable in to your function with a default argument. Changing the definition of saveData to pass uuidString as a default argument, as:
def saveData(uuidString=uuidString):
will make it work as you expect.

Python default params confusion

I just started learning Python and I'm confused about this example:
def append_to(element, to=None):
if to is None:
to = []
to.append(element)
return to
If to was initialized once, wouldn't to not be None the 2nd time it's called? I know the code above works but can't wrap my head around this "initialized once" description.
def append_to(element, to=None):
to = ...
to here becomes a local variable and is assigned to a list, and is deallocated if you don't assign return value to another variable.
If you want to staying alive for subsequent calls to append_to you should do:
def append_to(element, to=[]):
to.append(element)
return to
Demo:
>>> lst = append_to(5)
>>> append_to(6)
>>> append_to(7)
>>> print lst
[5, 6, 7]
If "to" was initialized once, wouldn't "to" won't be "None" the 2nd time it's called?
to would become None if you don't pass a value for it: append_to(1) and only when to is None will your code rebind the local name to to a newly created list inside the body of your function: to = [].
The default values of functions are assigned only once, that's whatever you assign as a default value, that object will be used for every call you make to the function and will not change, typically the same reference of the default value will be used for every call you make to the function. This matters when you assign mutables as defaults:
l = []
def defArgs(d=l) # default arguments, same default list for every call
d.append(1)
return d
defArgs() is l # Object identity test: True
Run the above function multiple times and you will observe the list growing with more elements because you get only one single copy of argument defaults for every function shared by every function call. But notice here:
def localVars(d=None):
if d is None:
d = [] # Different list for every call when d is None
d = [] is executed every time you call localVars; when the function finishes its job, every local variable is garbage-collected when reference count drops to 0, but not the default values of arguments, they live after the execution of the function and are not typically garbage-collected after the execution of the function.
In Python there is no declaration and initialization stage when you use a variable. Instead, all assignment is a definition where you bind an instance to the variable name.
The interpreter bind the instance to the default value when instantiate the function. When the default value is a mutable object and you only change its state by its methods than the value will be "shared" between calls.

Python and closed variables [duplicate]

This question already has answers here:
nonlocal keyword in Python 2.x
(10 answers)
Closed 6 months ago.
Have a look at this code:
def closure():
value = False
def method_1():
value = True
def method_2():
print 'value is:', value
method_1()
method_2()
closure()
I would expect it to print 'Value is: True' but it doesn't. Why is this and what is the solution?
This happens because method_1 gets its own local scope where it can declare variables. Python sees value = True and thinks you're creating a new variable named value, local to method_1.
The reason Python does this is to avoid polluting the outer scope's locals with variables from an inner function. (You wouldn't want assignments in regular, module-level functions to result in global variables being created!)
If you don't assign to value, then Python searches the outer scopes looking for the variable (so reading the variable works as expected, as demonstrated by your method_2).
One way to get around this is by using a mutable object instead of assigment:
result = { 'value': False }
def method_1():
result['value'] = True
In Python 3, the nonlocal statement (see also docs) was added for exactly this scenario:
def method_1():
nonlocal value
value = True # Works as expected -- assigns to `value` from outer scope
In method_1, Python assumes (quite sensibly!) that value is a local variable. Whenever you assign to a variable name inside a function, it is assumed that that variable name is a new local variable. If you want it to be global, then you have to declare it as global, and if you want it to be "nonlocal", in Python 3, you can declare it nonlocal, but in Python 2, you have to do something uglier: store the value in a container. That avoids having to reassign the variable name, and so avoids the scoping ambiguity.
def method_1_global():
global value
value = True
def method_1_nonlocal_P3():
nonlocal value
value = True
value = [False]
def method_1_nonlocal_P2():
value[0] = True
When you assign to a variable, it assumes the variable is of local scope. So the value in method_1 is not the value in closure.
If you want this to work on Python 3, add a line to method_1: nonlocal value.
On Python 2,
def closure():
value = [False]
def method_1():
value[0] = True
def method_2():
print 'value is:', value
method_1()
method_2()
closure()
is one possible work-around.
This is because you are not actually modified the closed-over variable - you are masking it with a new one that has the same name. In python 2.x there is no easy way around this, which is why the nonlocal keyword was added in python 3.
This can be worked around, however, using mutable types like list and dictionary. There is a good example in this answer.
To avoid this, you can use a list.
value = [False]
def method_1():
value[0] = True
What Python does now is searching in higher levels of the scope as value is not available in the local variables. As value is a list and Python refernces it as a global variable relative to *method_1*, you can treat value as it is, a list.

Categories

Resources