2

I have found that the scope of a variable referencing a List is different than the variable referencing a Tuple or Integer or String. Why does it happen ?

1) When I am using an Integer, String or Tuple:-

>>> def foo(anInt, aStr, aTup):
         anInt += 5
         aStr  += ' hi'
         aTup  += (12,)
         print (anInt,aStr,aTup)
>>> anInt, aStr, aTup = 5, 'Fox', (11,)
>>> foo(anInt, aStr, aTup)
10 Fox hi (11, 12)
>>> print (anInt, aStr, aTup)
5 Fox (11,)

2) When i am using an List:-

>>> def foo(aList):
        aList.append(2)
        print (aList)
>>> aList = [1]
>>> foo(aList)
[1, 2]
>>> print (aList)
[1, 2]

In the first case changes in the values of anInt, aStr, aTup is limited to the scope of function while in case of a List scope changes.

'

4
  • 1
    List is a mutable datatype in python. String,integer tuple etc are not. Commented Apr 6, 2017 at 6:38
  • So how does mutability affect the scope? Commented Apr 6, 2017 at 6:44
  • 1
    The scope is working exactly the same in both examples. In the first example, you use the augmented assignment operator += which for immutable data-structures returns and reassigns a new object to the local variable. Outside this local scope, these effects are no longer visible. In the second example, you use .append, which mutates the list in place, and the effect will be visible in any scope where the list is accessible (i.e. your global scope). Commented Apr 6, 2017 at 7:13
  • Furthermore, because it bears repeating: Python's evaluation strategy is not different for mutable or immutable types either. It works exactly the same. The technical name for Python's evaluation strategy is call by sharing. This is sometimes called "call by object value" or even just "call by value", but it is certainly not like call-by-reference, or else you would be able to write a true swap function in Python. Commented Apr 6, 2017 at 7:19

3 Answers 3

3

It is not a question of scope. The scope does not differ between types. It can not differ, in fact: a variable is just a named reference to a value, completely type-agnostic.

But some types in Python are mutable (once created, a value can be changed), and list is one of them. Some are immutable, and changing a value of these types requires creating a new object. += works in two different ways for mutable and immutable types. For immutable types a += b is equivalent to a = a + b. A new object is created and the a variable now refers to it. But the objects of mutable types are modified "in place", quite as your list. You may want to read this.

Now let's have a look at scopes. Here you have global objects that are passed to a function. The function uses them as its parameters, not as global objects (yes, aList inside the function is not the same variable as aList outside it, here is more information on scopes). It uses other references to the same objects, and it can not modify the variables, it can just modify objects the variables refer to. But only the mutable objects.

You may be surprised if you compare the results of two following code samples.

>>> a = 1; a1 = a; a1 += 1; a
1

>>> a = [1]; a1 = a; a1 += [1]; a
[1, 1]

What is the difference? The only difference is that int is an immutable type, and the list is a mutable one. After assigning a to a1 they always refer a single object. But the += operator creates a new object in case of int.

Sign up to request clarification or add additional context in comments.

Comments

1

Here is a good example of what looks like a difference in scope, when using a variable referring to an int, vs a list. Note the 'prev' variable:

 1 class Solution:
 2    def convertBST(self, root: TreeNode) -> TreeNode:
 3        
 4        if root == None:
 5            return root
 6        
 7        prev = 0
 8        def traverse(node):
 9            if node.right:
10                traverse(node.right)
11            node.val += prev
12            prev = node.val
13            if node.left:
14                traverse(node.left)
15                
16        traverse(root)
17        return root

This errors with the following message:

UnboundLocalError: local variable 'prev' referenced before assignment
    node.val += prev

There is no error, if instead I replaced these lines with this code:

line 7: prev = [0]

line 11: node.val += prev[0]

line 12: prev[0]= node.val

making one believe that prev = 0 was invisible within the traverse function, while prev = [0] is visible!

But in fact, prev = 0 is also visible and can be used within the traverse function, if no assignment to it takes place i.e. UnboundLocalError occurs on line 11, only if line 12 is present. Because of the immutability of an int, line 12 causes the variable prev within the traverse function to point to a new memory location, and this then "hides" the variable prev = 0 defined outside, causing the error.

But when a new int is assigned to prev[0], an element in a mutable list, the pointer for the first element of the list can be updated to point to the new int (both inside and outside the traverse function); no separate scope (ie local variable) is created, the original prev variable remains visible within the traverse function, and prev[0] can be used on line 11 before the assignment on line 12.

Comments

-3

The difference is that the immutable types int, string and tuple are passed by value. When the function updates the value, it is updating a local copy.

The list is passed by reference. When you use append, it is updating the original list.

3 Comments

This is false. Python's evaluation strategy is not different for mutable or immutable types. It works exactly the same. The technical name for Python's evaluation strategy is call by sharing. This is sometimes called "call by object value" or even just "call by value", but it is certainly not like call-by-reference, or else you would be able to write a true swap function in Python.
I just wanted to identify that it's not a question of scope but how the values are updated.
I'm sorry, but it's still incorrect. First, your claim about the difference in evaluation strategy for mutable and immutable types is simply wrong. Second, you aren't updating a "local copy", the variables still hold references to the same immutable object. Objects are never copied with assignment in Python. The difference is that in one case you are mutating an object, in another, you are reassigning a local variable to reference a new object when you use the augmented assignment operator (+=).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.