Is this not against the Python rules of local vs global?

In this little script the main() modifies alldata although it is not set as global within main():

#! /usr/bin/env python3
# -*- coding: UTF-8 -*-

alldata = {"init" : 123}

def main():
    alldata.update({"main":987})

if __name__ == '__main__':
    print("alldata", alldata)
    main()
    print("alldata", alldata)

creating output:

alldata {'init': 123}
alldata {'init': 123, 'main': 987}

Isn’t this not against the Python rules, which I think do require that alldata within main() should be treated as local?

No, alldata is a global variable in main(). It would be a local variable if the code in main() assigned to it (e.g. alldata = 42) without declaring global alldata.

1 Like

No, that’s exactly what the Python rules are.

Only assignment to the name “alldata” inside the function will make the interpreter treat it as a local variable. You have no assignment, just a method call.

The method call modifies the object, so it is behaving as designed.

3 Likes

You have no assignment, just a method call.

That seems to be the key reason.

Although it does feel a bit like a backdoor to make changes. I’d have preferred if changes weren’t allowed at all unless a variable is explicitly made global.

Thanks for the explanation.

That’s not possible: it confuses the two concepts of objects (values) and names (variables):

  • variables (names) have a scope (local or global);
  • objects (values) are immutable or mutable.

The interpreter can control the scope of variables. Python’s rules for scoping a variable name inside a function includes the rules:

  • name binding operations (assignment and del name) make the variable local to the function;
  • unless the name is declared global;
  • otherwise the name is treated as global.

(This is a simplified version, as I have left out “nonlocal” variables and a few other factors, such as what happens inside a class.)

But the interpreter cannot control whether the variable’s value, the object, is immutable. Only the object itself does that.

The interpreter can’t know whether obj.method() mutates the object, or performs some other action.

Although Python does allow, and encourage, many functional programming idioms, it does not promise that all objects are immutable. Many important builtins, such as lists and dicts, are mutable, and if you can access the object, you can mutate it.

That’s what mutable objects do: they have methods which modify themselves.

The interpreter can’t say “oh, this object isn’t declared global, so I won’t allow this method to run”. Objects aren’t declared global, only names are. And the interpreter cannot know which methods mutate the object and which don’t.

Thanks for giving this deeper insight. I thing I see the logic now, yet I’d wish …

Well, wishes don’t always come true :smile:

Thinking about it: I could put all my global variables into a class, and change them to my hearts delight at any place without having to bother to put all the global xyz statements into all the functions?

Indeed, using a class to store my variables let me overcome the need to put all the global statements into my code:

#! /usr/bin/env python3
# -*- coding: UTF-8 -*-

class globs():
    var1 = 1
    var2 = None
    var3 = []

g = globs()

def modify():
    g.var1 = 99
    g.var2 = "my globals"
    g.var3 = {"abc" : 123}

def main():
    print("var1:", g.var1, ", var2:", g.var2, ", var3:", g.var3)
    modify()
    print("var1:", g.var1, ", var2:", g.var2, ", var3:", g.var3)

if __name__ == '__main__':
    main()

gives this output:

var1: 1 , var2: None , var3: []
var1: 99 , var2: my globals , var3: {'abc': 123}

All I have to is to name the class and to instantiate it.

The need to precede each variable intended to be global with a “g.” is an additional advantage, because it assures me that this really is a global variable, and not one I had forgotten in a global statement or missed through a typo or whatever else.

I have modified my real script accordingly, and it is working very nicely.