1

I am new to python so there probably is a simple answer but I am not even sure what to search for. Here's a simplified code snippet:

testing = 1
print testing

def update():
    print "UPDATED"
    testing = 2

update()
def test():
    new = testing
    print new

test()

My expectation would be that the last print would be "2" but instead it is "1". Why does this happen?

I need this so that I can check if the unix time from init/update of a certain variable is the same as when a particular function (with while loop within) started executing. Let me know if there's a better way to achieve this.

DominicM
  • 6,520
  • 13
  • 39
  • 60
  • To fully understand the issue, you want to read [this](http://docs.python.org/2/reference/executionmodel.html#naming-and-binding) carefully. – Elazar Oct 28 '13 at 22:42

4 Answers4

3

If you're going to use global variables (which is a bad idea in general, and must be avoided if possible), you have to indicate inside each function that modifies testing that the variable is global, like this:

def update():
    global testing # mandatory: the variable is being modified
    print "UPDATED"
    testing = 2

It's not necessary to explicitly use global in test() - we're only reading the value, whereas we're changing it in update(), but it's useful as a remainder that the variable was defined globally

def test():
    global testing # not mandatory, but useful as documentation
    new = testing
    print new
Óscar López
  • 232,561
  • 37
  • 312
  • 386
  • But there's no strict need for `global` in `test()`, except as a documentation thing (or possibly to prevent future mistakes). – Elazar Oct 28 '13 at 22:29
  • 1
    @Elazar true, I updated my answer clarifying this – Óscar López Oct 28 '13 at 22:32
  • Since it's a bad idea to use globals, can you think of an alternative in my scenario? – DominicM Oct 28 '13 at 23:00
  • 1
    @DominicM pack the functions inside a class, and declare `testing` as an attribute. Or pass the shared value around as input parameters / returned values. In practice globals can be avoided most of the time, and there will always be cleaner, simpler alternatives – Óscar López Oct 28 '13 at 23:06
1

you have to declare your variable global in the function (write global testing).

orange
  • 7,755
  • 14
  • 75
  • 139
1

testing is a local variable to the update() function. Function locals are entirely separate from module globals.

Python marks a name as a local if you assign to it within a scope. testing is assigned to within update(). new is also assigned to in test(), but in that function testing is not. That makes testing, in test, a global. That's how python lets you find built-ins, functions and other objects declared in your module or imported from another module.

If you want to assign to a name in a function and for it to be treated as a global still, you need to explicitly override Python and tell it to treat the name as a global:

def update():
    print "UPDATED"
    global testing
    testing = 2

You can place the global statement anywhere in the function, it'll make that specific name global for the whole function.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • "Python marks a name as a local when you assign to it" - is a bit misleading. Better "if you assign to it anywhere in this scope" or something like that. "When" is what many programmers *think* it is. – Elazar Oct 28 '13 at 22:32
  • The point is that the word "when" implies time for the reader, but the issue is a lexical one. Maybe s/when/where/ would do. (Sorry for being picky on the issue. I think it's a subtle one; took me a while to get it myself). – Elazar Oct 28 '13 at 22:36
  • 1
    @Elazar: I replaced it with `if` instead. – Martijn Pieters Oct 28 '13 at 22:39
0

I'll try to add something to the good answers Oscar and Martijn already gave.

When you read a function in python, any function, you have to read it twice. Always.

In the first pass, you should do this: for each global and nonlocal declaration statement, move them (conceptually) to the beginning of the function.

For each statement of the following forms

* x = expr (or "x += expr", "x *= expr", etc.)
* for x in expr:
* with expr as x:
* def x(...):
* class x:
* import x (or "import foo as x")
* from some_module import x (or "from some_module import foo as x")

Take the name x, and see: if it is declared in a global or nonlocal statement, leave it there. Otherwise, add it to a local declaration, right after the global and nonlocal. (There's no local keyword in python. It's all conceptually). Function parameters are always local. The names in these statements should be mutually exclusive, just like the scopes they refer to.

This was the first pass. Now you can sit and read the code. Any time you see a variable name, you look it up on these three statement - global, nonlocal and our imaginary local. If the name is there - you know where it belongs. Otherwise, look it up in the same way on any enclosing function, then in the global namespace. If it's not there, it should be a built in - or an error.

Having done that for update(), you'd get:

def update():
    local testing # not real python code
    print "UPDATED"
    testing = 2

Well, that's not what you meant, right?

Another example, before:

x=3
def print5():
    for i in range(5):
        print(x)
        x += 1

After:

x=3
def print5():
    local i, x
    for i in range(5):
        print(x) # oops. x is local but was not assigned!
        x += 1

Note that the algorithm I described is not full. I left out exceptions, which hide names for the except clause only; eval and exec; and from some_module import *.

For complete information, see the docs: http://docs.python.org/2/reference/executionmodel.html#naming-and-binding

Elazar
  • 20,415
  • 4
  • 46
  • 67