3

I have a question about the name scope of variables, I've tried to find the answer here and I got a similar one but still little confused here Function changes list values and not variable values in Python

So the code is as follows:

position = [50, 50]

# Handler for timer
def tick():
    x = random.randrange(0, width)
    y = random.randrange(0, height)
    position[0] = x
    position[1] = y

Here why don't I need to add "global position" in order to change elements of the variable position?

Community
  • 1
  • 1
Enkri_
  • 161
  • 2
  • 12

3 Answers3

1

Adding global position would only be necessary if you were assigning the global position variable to refer to a new list (without the global declaration, position would written into the locals for the function).

In your situation, all you are doing is looking up the global variable position to find which list it refers to. Then you're updating that list in-place.

Here is a Python tutor visualization that shows all the variations (assign to a local, assign to a global, update a list referred to by a global).

Also, have a look at Ned Batchelder's nice blog post that fully explains python's pass-by-object mechanism.

Raymond Hettinger
  • 216,523
  • 63
  • 388
  • 485
  • so you mean here the elements of position is automatically updated without the necessity of declaring it as global? I'm kinda struggling to accept this concept... – Enkri_ Aug 04 '15 at 04:13
  • Yes. Think of it this way. Imagine you have a global variable called *position* that is a connection to a database. You can lookup that variable without a *global* declaration (because there is no local of the same name). Then you can use that connection to modify the database but no change the connection itself. When would need a *global* declaration? Only if you were assigning a new connection to *position*. In the first case, *position* remains unchanged and database changes. In the second case, *position* is changed to a different connection. – Raymond Hettinger Aug 04 '15 at 04:30
  • Thanks, but how can I know the cod that I provided initially was of which case? – Enkri_ Aug 06 '15 at 00:55
1

From documentation of Naming and Binding -

If a variable is used in a code block but not defined there, it is a free variable.

From the documentation of global statement -

The global statement is a declaration which holds for the entire current code block. It means that the listed identifiers are to be interpreted as globals. It would be impossible to assign to a global variable without global, although free variables may refer to globals without being declared global.

(Emphasis mine)

When you do -

position[0] = <something>

You are not defining anything, you are mutating the already defined list position inplace (changing its 0th element to refer to something else) .

Lets assume if position was not defined ever, when you do something like -

position[0] = 1

You will get NameError -

>>> position[0] = 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'position' is not defined

That is why, even though you do postion[0] = <something> , its still a free variable, as its not defined in the block, and hence it is treated as global.


UPDATE :

More info , when you do -

position[0] = <something>

This is not a simple assignment statement , its actually an augmented assignment statement .

Anand S Kumar
  • 88,551
  • 18
  • 188
  • 176
  • so you mean here the elements of position is automatically updated without the necessity of declaring it as global? I'm kinda struggling to accept this concept... – Enkri_ Aug 04 '15 at 04:13
1

You need to apply two concepts for understanding your code

  • Python Object Model
  • Scoping

First the Scoping Rules:

Following are hierarchy of Scoping Rules for Variables

  1. Local Scope - Inside the function
  2. Enclosing - Inside any or all of Enclosing Function
  3. Global - Top Level Module ( File )
  4. Built In's - Any BuiltIn Modules which are being imported

Python Object Model :

In Python, everything is a object. When an variable is declared say x = 1000, following steps happens

  • int object with Value 1000 is created
  • object reference x is created
  • x is pointed to that integer object

Now, when statement position[0] = x is executed inside tick() , the statement doesn't have initialization part only assignment. Since position is not defined inside function scope, it looks for in order of Hierarchy of Scoping Rules - the Enclosing Scope (Parent Function) followed by Module Scope ( File ) followed by BuiltIn Modules. Over here it finds the variable in Global scope and updates it.

In-case your statement is position = [100, 100] , a new list object is created and new object reference is created within scope of tick() function and performs the assignment

If you have global position statement, it tells Python that variable in Global Scope is same as variable in Local Scope. This followed by position = [100,100] creates a list object [100,100] and links it global variable position to the new list object.

Manuel
  • 287
  • 5
  • 15
  • Thanks, but I'm still a little confused here, so you say when I do "position[0] = x" the object position is available in global scope, I agree with that, but when I do "position = [100, 100]", isn't object position still available in global scope? How come it creates a new object? – Enkri_ Aug 06 '15 at 01:00
  • @Enkri_ , when `position = [100,100]` is executed both initialization & assignment occurs. so position is defined in local scope and assignment occurs. When `position[0] = x` is executed only assignment happens. There is no initialization , it starts searching in order of hierarchy of scoping rules, finds a match in Global scope and updates it. I have updated my answer to reflect the same, – Manuel Aug 06 '15 at 03:12