I'm using a recursive function to sort a list in Python, and I want to keep track of the number of sorts/merges as the function continues. However, when I declare/initialize the variable inside the function, it becomes a local variable inside each successive call of the function. If I declare the variable outside the function, the function thinks it doesn't exist (i.e. has no access to it). How can I share this value across different calls of the function?
I tried to use the "global" variable tag inside and outside the function like this:
global invcount ## I tried here, with and without the global tag
def inv_sort (listIn):
global invcount ## and here, with and without the global tag
if (invcount == undefined): ## can't figure this part out
invcount = 0
#do stuff
But I cannot figure out how to check for the undefined status of the global variable and give it a value on the first recursion call (because on all successive recursions it should have a value and be defined).
My first thought was to return the variable out of each call of the function, but I can't figure out how to pass two objects out of the function, and I already have to pass the list out for the recursion sort to work. My second attempt to resolve this issue involved me adding the variable invcount to the list I'm passing as the last element with an identifier, like "i27". Then I could just check for the presence of the identifier (the letter i in this example) in the last element and if present pop() it off at the beginning of the function call and re-add it during the recursion. In practice this is becoming really convoluted and while it may work eventually, I'm wondering if there is a more practical or easier solution.
Is there a way to share a variable without directly passing/returning it?
There's couple of things you can do. Taking your example you should modify it like this:
invcount = 0
def inv_sort (listIn):
global invcount
invcount += 1
# do stuff
But this approach means that you should zero invcount before each call to inv_sort.
So actually its better to return invcount as a part of result. For example using tuples like this:
def inv_sort(listIn):
#somewhere in your code recursive call
recursive_result, recursive_invcount = inv_sort(argument)
# this_call_invcount includes recursive_invcount
return this_call_result, this_call_invcount
There's no such thing as an "undefined" variable in Python, and you don't need one.
Outside the function, set the variable to 0. Inside the loop, use the global keyword, then increment.
invcount = 0
def inv_sort (listIn):
global invcount
... do stuff ...
invcount += 1
An alternative might be using a default argument, e.g.:
def inv_sort(listIn, invcount=0):
...
invcount += 1
...
listIn, invcount = inv_sort(listIn, invcount)
...
return listIn, invcount
The downside of this is that your calls get slightly less neat:
l, _ = inv_sort(l) # i.e. ignore the second returned parameter
But this does mean that invcount automatically gets reset each time the function is called with a single argument (and also provides the opportunity to inject a value of invcount if necessary for testing: assert result, 6 == inv_sort(test, 5)).
Assuming that you don't need to know the count inside the function, I would approach this using a decorator function:
import functools
def count_calls(f):
#functools.wraps(f)
def func(*args):
func.count += 1
return f(*args)
func.count = 0
return func
You can now decorate your recursive function:
#count_calls
def inv_sort(...):
...
And check or reset the count before or after calling it:
inv_sort.count = 0
l = inv_sort(l)
print(inv_sort.count)
Related
I need to change a variable each time the function is called.
My function counts the number of time this function has been called:
def score_chart():
num_of_charts=+ 1
return num_of_charts
At the beginning num_of_charts equals 0. Then I call the function and re-save num_of_charts to be equal 1.
But if I call it second time, the result is still 1, while I m expecting to get 2.
num_of_charts = 0
num_of_charts = score_chart()
print (num_of_charts)
num_of_charts = score_chart()
print(num_of_charts)
1
1
Could you please help
Use a parameter to get the old value.
def score_chart(num):
return num + 1
num_of_charts = 0
num_of_charts = score_chart(num_of_charts)
print(num_of_charts)
num_of_charts = score_chart(num_of_charts)
print(num_of_charts)
A general and useful way to count calls to any function is to define and use a function decorator. The decorator function maintains a count of the calls and this can be accessed whenever required.
def call_counter(func):
def keeper():
keeper.calls += 1
return func()
keeper.calls = 0
return keeper
#call_counter
def score_chart():
pass # function could do anything required
for i in range(4):
score_chart()
print(score_chart.calls)
which prints 4
The statement: My function counts the number of times this function has been called:
def score_chart():
num_of_charts=+ 1
return num_of_charts
is not true. Your function assigns to num_of_charts the value + 1.
What you actually intended to do is to increase the value by one using num_of_charts += 1 but had put the + at the right side of = instead of the left side.
This is the reason why the returned result was always 1.
In addition to this above you need to know that in Python it is important to be aware of the local and global scope of variable names used in a function (check out the docs on it). Not being aware of that leads very often to confusion and wrong expectations. stackoverflow is full of questions which resulted from this kind of confusion.
As long as you only use a variable name in a function it will represent the global value assigned outside of the function before calling it. This is the reason why:
def score_chart_1():
return num_of_charts
is able to return the current value assigned to num_of_charts outside the function so that:
num_of_charts = 2
print(score_chart_1())
num_of_charts = 3
print(score_chart_1())
will print 2 and 3.
If you in your function try to assign a value to a variable name using num_of_charts += 1 the variable name changes its scope from global to local in the function.
Notice that num_of_charts += 1 is equivalent to num_of_charts = num_of charts + 1 and Python needs to know the value of num_of_charts to increase it by one. But usage of the assignment operator = with num_of_charts changed the scope of num_of_charts to local where num_of_charts does not yet have a value and because of that an:
UnboundLocalError: local variable 'num_of_charts' referenced before assignment
will be raised.
The Error can be avoided by declaring the scope of a variable to be global in the function using as first function statement (as first is the usual convention in Python):
global num_of_charts
With this statement above there will be no UnboundLocalError and the function will then work as expected.
Even is this changes will solve your problem this kind of solution is considered to be a bad programming style and the approach of passing a counter parameter to the function and updating it with the return value in order to pass the changed value on next function call a better one (see the answer posted by Barmar for code of this approach).
Best approach to achieve what you want would be to use a Python class where it would easily be possible to count any function calls. See code below for an example of code (choice of variable names should be sufficient as explanations what the code does):
class Charts:
def __init__(self, list_with_charts):
self.list_with_charts = list_with_charts
self.list_with_scores = [0]*len(list_with_charts)
def score_chart(self, chart_index):
return self.list_with_scores[chart_index]
def show_chart(self, chart_index):
# some code to present/show the chart
self.list_with_scores[chart_index] += 1
objCharts = Charts(['chart0.ppt', 'chart1.ppt', 'chart2.ppt'])
objCharts.show_chart(0)
objCharts.show_chart(0)
objCharts.show_chart(2)
objCharts.show_chart(2)
objCharts.show_chart(2)
print(objCharts.score_chart(2)) # gives 3
For the sake of completeness it is worth to mention two another kinds of approaching counting function calls (see the first version of the answer by user19077881 in the history of edits):
using a function closure
using a function decorator
Notice that if your intention would be to only count calls to a function somehow 'hiding' the fact of counting, the function closure would be probably the best way to go.
METHOD 1:
You can use global variables for them to be accessible everywhere in the code (sometimes can be a bad practice)
Code:
def score_chart():
global num_of_charts
num_of_charts += 1
METHOD 2:
You can use num_of_charts as a function argument and return it at the end of the function (you need to store that value in another variable outside the function)
Code:
def score_chart(num_of_charts):
num_of_charts += 1
return num_of_charts
new_num = score_chart( # put a number here )
After 1st function call you have to pass that new_num as an argument and overwrite it with another value (only if you don't need to go back to previous values)
new_num = score_chart( # put a number here )
new_num = score_chart(new_num)
Also keep in mind that it is += 1 and not =+ 1
For example:
def increment(number):
... def inner_increment():
... return number + 1
... return inner_increment()
The inner_increment() can be taken out and defined in the same scope as increment(). Is that true?
EDIT:
def increment(number):
return inner_increment(number)
def inner_increment(number):
return number+1
"Always" and "safely" are very sweeping words, involving a variety of assumptions and perhaps a value judgement or two.
As you have done, yes, it's possible to extract a nested function -- provided that you move all of its required outer scope collateral into the parameter list. Anything that inner assumed from the outer scope, now must be passed as an argument.
You also need to ensure that you don't collide with any other inner_increment symbol in the outer scope.
This question already has answers here:
What is the Python equivalent of static variables inside a function?
(28 answers)
Closed 3 years ago.
Is there a way that a function can remember its previous output and use that value during the next call to the function? For instance, assume there is a function, runningTotal with a single argument x that returns x on the first call to runningTotal but x + prevOutput for every call after that. Is there a way to write such a function in python?
I am aware that this could be easily achieved by using a global variable in the function or by saving the previous value to a new variable, but I would like to avoid these solutions if possible. The reason I'm looking for an alternate solution is because this is one function in a program I'm working on with other people and I would like to avoid having to create more global variables than already established.
Yes, but to avoid too much hackery or GLOBAL variables we'll probably want to use a class.
In python a class can be treated as function with a magic function (method) inside the class named __call__.
Your question might be better written: what's the best way to have a function in python that has internal state?
Let's say we have the runningTotal function defined using a global variable as:
TOTAL = 0
def runningTotal(inc):
global TOTAL
TOTAL += inc
return TOTAL
Answer Ok so lets define a class that will behave the same way as the above function but without a global variable:
class StatefulFunction:
running_total = 0
def __call__(self, inc):
self.running_total += inc
return self.running_total
# create the stateful function variable
runningTotal = StatefulFunction()
# Use the stateful function
runningTotal(1)
# outputs: 1
runningTotal(5)
# outputs: 6
Another way to accomplish the same thing is with a Counter Dictionary
from collections import Counter
counter = Counter()
counter['runningTotal'] += 1
# in another part of the program
counter['runningTotal'] += 5
The output will be:
print(counter)
Counter({'runningTotal': 6})
Although there are ways of doing what you ask, it's not a good idea. As #JohnColeman pointed out, Simulate static variables in python with closures
But why not create a class?
class Accumulator:
total = 0
#classmethod
def add(cls, x):
cls.total += x
return cls.total
print(Accumulator.add(1))
print(Accumulator.add(2))
print(Accumulator.add(3))
Result:
1
3
6
You can set up a generator to maintain state and send values to it as well, as suggested by #HeapOverflow:
def get_running_total():
def _running_total():
value = 0
while True:
value += yield value
# get a generator instance
generator = _running_total()
# set it up to wait for input
next(generator)
# return the send method on the generator
return generator.send
# you can get a generator that functions similar to the Accumulator method
running_total = get_running_total()
print(running_total(1)) # prints 1
print(running_total(2)) # prints 3
print(running_total(3)) # prints 6
This question already has answers here:
Function not changing global variable
(4 answers)
Closed 3 years ago.
counter = 0
def addCounter():
counter = counter + 1
return counter
UnboundLocalError: local variable 'counter' referenced before assignment.
I'm trying to make a counter count every time this function runs. I've tried passing the counter variable in as a parameter as well, but that doesn't work either.
You need:
counter = 0
def addCounter():
global counter
counter = counter + 1
return counter
Explanation: in Python, the declaration of inner variables is implicit, an assignment automatically declares the values on the left-hand side, however that declaration is always in the local scope. That's why if you write this:
counter = 0
def addCounter():
return counter
it will work fine but as soon as you add an assignment
counter = 0
def addCounter():
counter += 1
return counter
it breaks: the assigment adds an implicit local declaration. global overrides this, although it requires that the global exist beforehand, it does not create a global, it just tells the function that this is a global variable it can reassign to.
I've tried passing the counter variable in as a parameter as well, but that doesn't work either.
Indeed not. Python's evaluation strategy is sometimes called "pass by sharing" (or "pass reference by value") which is technically "pass by value" but that term gets a bit confusing as the values in this case are references, the references are copied but the objects referred to are not, and thus the end-behaviour diverges from the normal expectations of "pass by value" expectations.
Using a class rather than global:
Another way to handle (not use) global variables is to wrap the functions and variables you wish to be global in a class.
While this is a little heavy for this specific case - classes add a host of functionality and flexability to the project. (Personally) highly recommended.
For example:
class Processor():
"""Class container for processing stuff."""
_counter = 0
def addcounter(self):
"""Increment the counter."""
# Some code here ...
self._counter += 1
# See the counter incrementing.
proc = Processor()
proc.addcounter()
print(proc._counter)
proc.addcounter()
print(proc._counter)
Output:
1
2
Sorry if this is a dumb question, but I've looked for a while and not really found the answer.
If I'm writing a python function, for example:
def function(in1, in2):
in1=in1+1
in2=in2+1
How do I make these changes stick?
I know why they dont, this has been addressed in many answers, but I couldn't find an answer to the question of how to actually make them do so. Without returning values or making some sort of class, is there really no way for a function to operate on its arguments in a global sense?
I also want these variables to not be global themselves, as in I want to be able to do this:
a=1
b=2
c=3
d=4
function(a,b)
function(c,d)
Is this just wishful thinking?
It can be done but I'm warning you - it won't be pretty! What you can do is to capture the caller frame in your function, then pick up the call line, parse it and extract the arguments passed, then compare them with your function signature and create an argument map, then call your function and once your function finishes compare the changes in the local stack and update the caller frame with the mapped changes. If you want to see how silly it can get, here's a demonstration:
# HERE BE DRAGONS
# No, really, here be dragons, this is strictly for demonstration purposes!!!
# Whenever you use this in code a sweet little pixie is brutally killed!
import ast
import inspect
import sys
def here_be_dragons(funct): # create a decorator so we can, hm, enhance 'any' function
def wrapper(*args, **kwargs):
caller = inspect.getouterframes(inspect.currentframe())[1] # pick up the caller
parsed = ast.parse(caller[4][0], mode="single") # parse the calling line
arg_map = {} # a map for our tracked args to establish global <=> local link
for node in ast.walk(parsed): # traverse the parsed code...
# and look for a call to our wrapped function
if isinstance(node, ast.Call) and node.func.id == funct.__name__:
# loop through all positional arguments of the wrapped function
for pos, var in enumerate(funct.func_code.co_varnames):
try: # and try to find them in the captured call
if isinstance(node.args[pos], ast.Name): # named argument!
arg_map[var] = node.args[pos].id # add to our map
except IndexError:
break # no more passed arguments
break # no need for further walking through the ast tree
def trace(frame, evt, arg): # a function to capture the wrapped locals
if evt == "return": # we're only interested in our function return
for arg in arg_map: # time to update our caller frame
caller[0].f_locals[arg_map[arg]] = frame.f_locals.get(arg, None)
profile = sys.getprofile() # in case something else is doing profiling
sys.setprofile(trace) # turn on profiling of the wrapped function
try:
return funct(*args, **kwargs)
finally:
sys.setprofile(profile) # reset our profiling
return wrapper
And now you can easily decorate your function to enable it to perform this ungodly travesty:
# Zap, there goes a pixie... Poor, poor, pixie. It will be missed.
#here_be_dragons
def your_function(in1, in2):
in1 = in1 + 1
in2 = in2 + 1
And now, demonstration:
a = 1
b = 2
c = 3
d = 4
# Now is the time to play and sing along: Queen - A Kind Of Magic...
your_function(a, b) # bam, two pixies down... don't you have mercy?
your_function(c, d) # now you're turning into a serial pixie killer...
print(a, b, c, d) # Woooo! You made it! At the expense of only three pixie lives. Savage!
# prints: (2, 3, 4, 5)
This, obviously, works only for non-nested functions with positional arguments, and only if you pass simple local arguments, feel free to go down the rabbit hole of handling keyword arguments, different stacks, returned/wrapped/chained calls, and other shenanigans if that's what you fancy.
Or, you know, you can use structures invented for this, like globals, classes, or even enclosed mutable objects. And stop murdering pixies.
If you are looking to modify the value of the variables you could have your code be
def func(a,b):
int1 = a + 2
int2 = b + 3
return int1,int2
a = 2
b = 3
a,b = func(a,b)
This allows you to actually change the values of the a and b variables with the function.
you can do:
def function(in1, in2):
return in1 + 1 , in2 + 1
a, b = function(a,b)
c, d = function(c,d)
python functions are closed -> when function(a,b) s called, a and b get reassigned to a local (to the function) references/pointers in1 and in2, which are not accessible outside of the function. provide references to those new values w/o using globals, you will need to pass that back through return.
When you pass an array or non primitive object into a function, you can modify the object's attributes and have those modifications be visible to other references for that object outside, because the object itself contain the pointers to those values, making the visible to anything else holding a pointer to that object.