Willman
Willman

Reputation: 438

Values Not Adding Up

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

Answers (3)

Totem
Totem

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

user2555451
user2555451

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

chepner
chepner

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 ints 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

Related Questions