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
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
When I code games with functions, I often get confused as to which variable to global. I've heard that globalizing variables isn't a very practice, so I try to minimize the amount by not globalizing any, and only globalize the ones that the error message tells me to. But doing that is quite annoying, and it wastes time. Can someone tell me the rule of thumb as to when we should global a variable in a function, and when it is not necessary? Here is a sample of what I mean (the functions):
import turtle
from random import randint as rd
from time import sleep
delay = 0.1
wn = turtle.Screen()
wn.setup(400,400)
wn.tracer(0)
player = turtle.Turtle('square')
player.penup()
player.goto(0,-170)
rock = turtle.Turtle('circle')
rock.shapesize(0.5,0.5)
rock.penup()
rock.goto(rd(-190,190),200)
rocks = [rock]
pen = turtle.Turtle(visible=False)
pen.penup()
pen.goto(0,150)
def go_left(): # No globalizing here
if player.xcor() >= -170:
player.setx(player.xcor()-10)
def go_right(): # No globalizing here
if player.xcor() <= 170:
player.setx(player.xcor()+10)
def move_rocks(): # No globalizing here
for rock in rocks:
rock.sety(rock.ycor()-rd(0,2))
def loop_rocks():
global count # rocks not globalized here
for rock in rocks:
if rock.ycor() < -200:
count += 1
rock.goto(rd(-190,190),200)
def add_rocks():
global count # rocks not globalized here
if count == len(rocks) and len(rocks) <= 15:
rock = turtle.Turtle('circle')
rock.shapesize(0.5,0.5)
rock.penup()
rock.goto(rd(-190,190),200)
rocks.append(rock)
count = 0
def write_score():
global time,score,high_score # pen not globalized here
time += 1
if time == 500:
score += 1
time = 0
if score > high_score:
high_score = score
pen.clear()
pen.write(f'Score: {score} High Score: {high_score}',align='center',font=('Arial',10,'bold'))
def hit(): # No globalizing here
for rock in rocks:
if player.distance(rock) < 15:
return True
def die():
global score,rocks # player not globalized here
sleep(1)
for rock in rocks:
rock.goto(300,300)
rocks = rocks[:1]
rocks[0].goto(rd(-190,190),200)
player.goto(0,-170)
score = 0
wn.listen()
wn.onkeypress(go_left,'Left')
wn.onkeypress(go_right,'Right')
score = 0
high_score = 0
count = 0
time = 0
while True:
if hit():
die()
move_rocks()
loop_rocks()
add_rocks()
write_score()
wn.update()
Style rules are not language rules. I.e. you shouldn't use eval(), but there it is, in the language.
tell me the rule of thumb as to when we should global a variable in a
function, and when it is not necessary?
The rules for when, and when not, to use global are simple, but even tutorials on the web get it wrong.
The global keyword should not be used to create a global
variable.
(Yes, that's partly a style rule.) When you define a top level variable outside a function, Python makes it global. (You don't use the global keyword for this.) When you assign to a variable inside a function, Python assumes it is local to the function. You only need the global keyword when you want change that later assumption so you can reassign (=) a global variable from within a function. You don't need the global declaration to examine a global variable. You don't need it to invoke a method on a global variable that might change its internal state or content:
You only need the global keyword when you want to reassign (=) a
global variable within a function.
The global declaration is used in any function where a global variable is reassigned. It is is placed ahead of the first reference to the variable, access or assignment. For simplicity, and style, global statements are put at the beginning of the function.
A statement like, "You should never use global variables", is a style rule and true of most programming languages -- apply it if/when you can. And if you absolutely can't, don't feel bad about it, just:
Comment all globals you do use properly.
Global constants are less an issue:
If global constants are truly constant, they never need the global
keyword.
#juanpa.arrivillaga's example of go_left() taking the additional values as parameters instead of global variables, fails to take into account that go_left() is a callback and that the turtle event assignment functions don't provide for additional parameters. (They should, but they don't.) We can get around this using a lambda expression (or partial function from functools), but when used this way, lambda isn't particularly great style either, IMHO.
#martineau's suggestion of "making them attributes of a class that the class' methods can access" (aka class variables) is fine, but what is left unsaid is that it means subclassing Turtle or wrapping a turtle instance with another class.
My personal issue with mutable globals is that they are problematic in a multi-threaded world.
Although it is not an answer, I just wanted to point out one more thing to look out for when shadowing names from outer scopes / global variables. cdlane writes in their answer that
You don't need the global declaration to examine a global variable.
I think it goes even further than that, because you cannot use the global keyword that way, as it is a declaration. As cdlane already said, it is used to declare variables in a local scope (such as a function or class) to be of global scope, such that you can assign new values to these variables from a local scope. You can even use the gobal keyword to declare new global variables from a local scope, although again, as cdlane pointed out, this is not a good idea. Here is some code highlighting these behaviours:
a = c = 1 # define global variables 'a' and 'b' and assign both the
# value 1.
class Local:
def __init__(self):
print(a) # if you only want to examine the global variable 'a',
# you don't need any keywords
self.declare_variables()
def declare_variables(self):
global a, b # declare 'a' and 'b' as global variables, such that any
# assignment statements in this scope refer to the global variables
a = 2 # reassign a new value to the already existing global
# variable 'a'.
b = 3 # assign a value to the previously undeclared global variable
# 'b'. you should avoid this.
c = 4 # this does not affect the global variable 'c', since the
# variable 'c' in this scope is assumed to be local by default.
local = Local()
print(a, b, c) # the vaules of the gobal variables 'a' and 'b' have changed,
# while 'c' remains unaffected.
So far nothing really new. However, when you are shadowing the names from global variables, but are still accessing the global variables elsewhere in the same scope, this becomes a problem.
If you declare a variable shadowing the name of a global variable before you try to access that global variable, all references to that variable name following that declaration will refer to the local variable. I think this might be the worse case, since this could go undetected and not produce any errors, but return wrong results.
If you try to declare a new local variable, or use the global keyword with the same variable name after you have already referenced that variable name in the same scope, it will result in an UnboundLocalError or SyntaxError, respectively.
def reference_global_before_local_declaration():
print(a) # first reference the global variable 'a'. this statement would
# work on its own if 'a' wouldn't be redeclared to be a local variable
# later on.
a = 5 # redeclare 'a' to be a local variable and assign it the value 5.
reference_global_before_local_declaration()
def reference_global_before_global_declaration():
print(a) # first reference the global variable 'a'. this statement would
# work on its own if 'a' wouldn't be declared to be a global variable
# again later on.
global a # declare 'a' to be a global variable again.
reference_global_before_global_declaration()
def reference_global_after_local_declaration():
a = 'text' # redeclare 'a' to be a local variable of type string and
# assign it the value 'text'.
b = a + 1 # here, 'a' was supposed to reference the global variable
# 'a', but is now referencing the local variable 'a' instead, due to 'a'
# being declared in the same scope and shadowing the name of the gobal
# variable 'a'.
reference_global_after_local_declaration()
The only way that I know of to avoid this, is to use the globals() function, although this really defeats all purpose and I wouldn't recommend it. I would however recommend to read PEP 3104 - Access to Names in Outer Scopes, which discusses these kinds of problems and presents a solution, which was ultimately never implemented though.
def reference_global_before_local_declaration():
print(globals()['a'])
a = 5
reference_global_before_local_declaration()
def reference_global_before_global_declaration():
print(globals()['a'])
global a
reference_global_before_global_declaration()
def reference_global_after_local_declaration():
a = 'text'
b = globals()['a'] + 1
reference_global_after_local_declaration()
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
Which is the correct use of global variables in Python 3?:
1) Stating global VAR_NAME once in the core script (not within a function) and then simply referring to the variable as VAR_NAME everywhere else
2) Stating global VAR_NAME once within every function that uses the global variable and then simply referring to the variable as VAR_NAME for the rest of the function and within the core script itself
In the first case the global keyword is pointless, so that is not correct. Defining a variable on the module level makes it a global variable, you don't need the global keyword.
The second example is correct usage.
However, the most common usage for global variables are without using the global keyword anywhere. The global keyword is needed only if you want to reassign the global variables in the function/method.
You need to use the global keyword in a function if you use the global variable in a way that would otherwise be interpreted as an assignment to a local variable. Without the global keyword, you will create a local variable that hides the global in the scope of the function.
Here are a few examples:
global_var = 1
def example1():
# global keyword is not needed, local_var will be set to 1.
local_var = global_var
def example2():
# global keyword is needed, if you want to set global_var,
# otherwise you will create a local variable.
global_var = 2
def example3():
# Without using the global keyword, this is an error.
# It's an attempt to reference a local variable that has not been declared.
global_var += 1
"in a way that would otherwise be interpreted as an assignment to a local variable" --- yes, but here is a subtle detail:
------------------- error: local variable 'c' referenced before assignment
def work():
c += 3
c = 0
work()
print(c)
------------------- error: local variable 'c' referenced before assignment
c = 0
def work():
c += 3
work()
print(c)
------------------- prints [3]
def work():
c.append(3)
c = []
work()
print(c)
------------------- prints [3]
c = []
def work():
c.append(3)
work()
print(c)
The main difference between the first two cases and the next two cases in the above answer would have to be the fact that the list is mutable. For cases like a = 1 a pointer points to the location where 1 is and when you say a = 2 the pointer shifts.
For the case of mutable objects a memory location is allotted and when methods like append are used changes occur to the memory location itself and so the value the mutable references is changed globally.
Now the big question is as to how the function knows the variable we are modifying is a global one or local one because it seems we can modify the global variable if its mutable and we cannot if its non mutable (The function also does not recognize this as the global variable)
This question already has answers here:
nonlocal keyword in Python 2.x
(10 answers)
Closed 7 months ago.
I have some code like:
def example():
# other logic omitted
stored_blocks = {}
def replace_blocks(m):
block = m.group(0)
block_hash = sha1(block)
stored_blocks[block_hash] = block
return '{{{%s}}}' % block_hash
num_converted = 0
def convert_variables(m):
name = m.group(1)
num_converted += 1
return '<%%= %s %%>' % name
fixed = MATCH_DECLARE_NEW.sub('', template)
fixed = MATCH_PYTHON_BLOCK.sub(replace_blocks, fixed)
fixed = MATCH_FORMAT.sub(convert_variables, fixed)
# more logic...
Adding elements to stored_blocks works fine, but I cannot increase num_converted in the second nested function. I get an exception that says UnboundLocalError: local variable 'num_converted' referenced before assignment.
I know that in 3.x, I could try nonlocal num_converted, but how can I solve the problem in 2.x? I don't want to use a global variable for this.
Problem: This is because Python's scoping rules are demented. The presence of the += assignment operator marks the target, num_converted, as local to the enclosing function's scope, and there is no sound way in Python 2.x to access just one scoping level out from there. Only the global keyword can lift variable references out of the current scope, and it takes you straight to the top.
Fix: Turn num_converted into a single-element array.
num_converted = [0]
def convert_variables(m):
name = m.group(1)
num_converted[0] += 1
return '<%%= %s %%>' % name
(see below for the edited answer)
You can use something like:
def convert_variables(m):
name = m.group(1)
convert_variables.num_converted += 1
return '<%%= %s %%>' % name
convert_variables.num_converted = 0
This way, num_converted works as a C-like "static" variable of the convert_variable method
(edited)
def convert_variables(m):
name = m.group(1)
convert_variables.num_converted = convert_variables.__dict__.get("num_converted", 0) + 1
return '<%%= %s %%>' % name
This way, you don't need to initialize the counter in the main procedure.
Using the global keyword is fine. If you write:
num_converted = 0
def convert_variables(m):
global num_converted
name = m.group(1)
num_converted += 1
return '<%%= %s %%>' % name
... num_converted doesn't become a "global variable" (i.e. it doesn't become visible in any other unexpected places), it just means it can be modified inside convert_variables. That seems to be exactly what you want.
To put it another way, num_converted is already a global variable. All the global num_converted syntax does is tell Python "inside this function, don't create a local num_converted variable, instead, use the existing global one.
What about using a class instance to hold the state?
You instantiate a class and pass instance methods to subs and those functions would have a reference to self...
I have couple of remarks.
First, one application for such nested functions comes up when dealing with raw callbacks, as are used in libraries like xml.parsers.expat. (That the library authors chose this approach may be objectionable, but ... there are reasons to use it nonetheless.)
Second: within a class, there are much nicer alternatives to the array (num_converted[0]). I suppose this is what Sebastjan was talking about.
class MainClass:
_num_converted = 0
def outer_method( self ):
def convert_variables(m):
name = m.group(1)
self._num_converted += 1
return '<%%= %s %%>' % name
It's still odd enough to merit a comment in the code...
But the variable is at least local to the class.
Modified from: https://stackoverflow.com/a/40690954/819544
You can leverage the inspect module to access the calling scope's globals dict and write into that. That means this trick can even be leveraged to access the calling scope from a nested function defined in an imported submodule.
import inspect
def get_globals(scope_level=0):
return dict(inspect.getmembers(inspect.stack()[scope_level][0]))["f_globals"]
num_converted = 0
def foobar():
get_globals(0)['num_converted'] += 1
foobar()
print(num_converted)
# 1
Play with the scope_level argument as needed. Setting scope_level=1 works for a function defined in a submodule, scope_level=2 for the inner function defined in a decorator in a submodule, etc.
NB: Just because you can do this, doesn't mean you should.