2

To avoid accidentally modifying a global variable, python requires an explicit global statement before assigning to a global variable. However, modifying a global variable by calling its method can be done without any extra statement:

x = [1, 2]
y = [1, 2]
def f():
  global x
  x = x + [3] # won't affect global variable without global statement
  y.append(3) # will affect global variable without global statement

This seemed slightly inconsistent. Is this design choice made because it's considered less dangerous / less of a bad practice to modify global mutable objects through a method call, compared to replacing them with an entire new object? If so, why?

max
  • 49,282
  • 56
  • 208
  • 355

3 Answers3

1

From the documentation:

In Python, variables that are only referenced inside a function are implicitly global. If a variable is assigned a value anywhere within the function’s body, it’s assumed to be a local unless explicitly declared as global.

In your case y is referenced inside of the function, thus implicitly global. On the other hand, x is assigned a value, thus it must be local unless explicitly declared otherwise.

And the documentation goes further to answer your questions:

Though a bit surprising at first, a moment’s consideration explains this. On one hand, requiring global for assigned variables provides a bar against unintended side-effects. On the other hand, if global was required for all global references, you’d be using global all the time. You’d have to declare as global every reference to a built-in function or to a component of an imported module. This clutter would defeat the usefulness of the global declaration for identifying side-effects.

Daniel
  • 11,332
  • 9
  • 44
  • 72
  • Ah so it's not that changing `y` in my question is safe, it's just that it's too hard for python interpreter to cleanly distinguish it from the relatively safe cases of accessing components of imported modules? – max Sep 16 '16 at 20:26
  • I don't think it's only hard for the Python interpreter, but for any interpreter. Essentially there is no difference beyond the one we create in our own minds. `Global` is also fundamental to avoid name collisions, otherwise we'd have to create different names for every function we'd write. – Daniel Sep 16 '16 at 20:35
  • This makes sense. – max Sep 16 '16 at 21:12
0

It's not really about mutability at the value level, which is how you're looking at it; it's about mutability of variable references, i.e. what a named item (a variable) is pointing at.

x = [1, 2]
print(id(x)) # 57226944
y = [1, 2]
print(id(y)) # 57262728
def f():
  global x
  x = x + [3]
  print(id(x)) # 57306648 - CHANGED
  y.append(3)
  print(id(y)) # 57262728 - UNCHANGED

f()

Note that the name 'x' is now pointing at a new thing (a newly-created list), whereas the .append operation on y has not changed what the name 'y' is pointing at.

ahri
  • 351
  • 2
  • 11
0

In one sentence:

"without declaring it explicitly, python will not let you change the reference of a global variable".

Now let's explain what you have just read, assignment of object to a variable is actually making a reference, which is a memory address to where the object lays in the memory.

When we write:

x = [1, 2]

What actually happens that somewhere in the memory the list object is allocated with all its function references and members and other shit. This address is what actually kept in x.

We may use the function id(object) to notice the change:

x = [1, 2]
def foo():
 print id(x)    # an address like 50075016
 y = [1, 2, 3]
 print id(y)    # another address like 50075272 
 x = y          # won't work without declaring 'global x' 
                # because we try to change the address stored in x
                # from 50075016 to 50075272.
 x.append(3)    # works
 print id(x)    # same address 50075016
Sawel
  • 929
  • 7
  • 17
  • Of course, but I guess my question was why it's less of a risk to modify the object compared to changing which object the variable points to. – max Sep 16 '16 at 20:41