Reputation: 438
Ok so here's my code:
tick = 1
week = 1
month = 1
year = 1
def new_week(week, tick):
week = week + 1
tick = tick + 1
if tick == 4:
new_month()
tick = 1
new_week(week, tick)
print week, tick
new_week(week, tick)
print week, tick
No matter how many subsequent times I tell it to run the new_week() function and print the values for 'week' and 'tick', it always prints 1 for both... Why is it disregarding the 'week = week + 1' line and the same for 'tick'??
I'm running Python 2.7 btw
Upvotes: 1
Views: 55
Reputation: 7349
This is about scope.
# here you are setting your variables, there scope is essentially global
tick = 1
week = 1
month = 1
year = 1
# we enter the function definition
def new_week(week, tick):
# you passed in your variables, and increment them
# however, they are only changed within the local scope of this function
# and since you didn't RETURN them as shown below, you are leaving the
# global variables from above, unchanged
week = week + 1
tick = tick + 1
if tick == 4:
new_month()
tick = 1
# when you print them out below, you are printing the unchanged globally scoped variables
new_week(week, tick)
print week, tick
new_week(week, tick)
print week, tick
In order to get the desired result you would do:
tick = 1
week = 1
month = 1
year = 1
def new_week(week, tick):
week += 1
tick += 1
if tick == 4:
new_month()
tick = 1
return (week, tick) #### return the variables
>>> week, tick = new_week(week, tick) ### capture the returned variables
>>> print week, tick
(2, 2)
Both tick
and week
will continue to increment each time you call new_week
. Obviously, tick
will reset once it reaches four. If you wanted to store the variables in a list
or dict
, then you could do the following:
Using a dict
:
vars = {'tick': 1, 'week': 1, 'month': 1, 'year': 1}
This now changes how your function can handle the data:
def new_week(lst=None):
if lst:
for key in lst:
vars[key] += 1
if vars['tick'] == 4:
new_month()
vars['tick'] = 1
>>> new_week(['week', 'tick'])
>>> print vars['week'], vars['tick']
2 2
Read about scope here:
https://www.inkling.com/read/learning-python-mark-lutz-4th/chapter-17/python-scope-basics
http://www.python-course.eu/python3_global_vs_local_variables.php
Upvotes: 1
Reputation:
The global names week
and tick
are being shadowed by the names of the function's arguments.
You need to remove these arguments (because they are unnecessary) and also declare week
and tick
as being global inside the function (this will allow you to modify their values):
tick = 1
week = 1
month = 1
year = 1
def new_week():
global week, tick # Declare that week and tick are global
week = week + 1 # Modify the value of the global name week
tick = tick + 1 # Modify the value of the global name tick
if tick == 4:
new_month()
tick = 1
new_week()
print week, tick
new_week()
print week, tick
Output:
2 2
3 3
Note however that a lot of Python programmers consider functions that use global declarations to be very inelegant. A much more pythonic approach would be to use a dictionary of values and have a single function for incrementing them.
Below is a very simple example to get you started:
dct = {
'tick': 1,
'week': 1,
'month': 1,
'year': 1
}
def increment(key):
dct[key] += 1
increment('week')
print dct['week']
increment('tick')
print dct['tick']
Output:
2
2
Upvotes: 3
Reputation: 531165
First, week = week + 1
just rebinds the (local) name week
to a new value; it does not affect the variable in the calling scope. However, since int
s are immutable, even week+=1
would not affect the calling scope; it would still just update the function-local week
variable.
One option is to use a dict
instead of individual variables. Since a dict
is a mutable object, you can pass the reference to the dict
as an argument, and changes to that object will be visible in the calling scope.
times = { 'tick': 1, 'week': 1, 'month': 1, 'year': 1}
def new_week(t):
t['week'] += 1
t['tick'] += 1
if t['tick'] == 4:
new_month(t)
t['tick'] = 1
new_week(times)
Upvotes: 1