0

Python works so that I can update a list in place every time a function runs:

list_obj = list()
def increase_list_obj(list_obj, n):
    list_obj.append(n)

print(list_obj)
for n in range(3):
    increase_list_obj(list_obj, n)
    print(list_obj)

OUTPUT:

[]
[0]
[0, 1]
[0, 1, 2]

Based on how the list persists I would expect that I can also update an int in place every time a function runs:

int_obj = 0
def increase_int_obj(int_obj):
    int_obj += 1

print(int_obj)
for n in range(3):
    increase_int_obj(int_obj)
    print(int_obj)

OUTPUT:
0
0
0
0

EXPECTED:
0
1
2
3

Why does the int update not work the same way as the list update? How are the persistence and scoping rules different for these two objects?

(I am NOT trying to suggest that the two should behave the same, I am curious about why they don't)

To preempt answers about how to update an int: I realize you can update the int value by just returning it from the function:

int_obj = 0
def increase_int_obj_v2(int_obj):
    int_obj += 1
    return int_obj

print(int_obj)
for n in range(3):
    int_obj = increase_int_obj_v2(int_obj)
    print(int_obj)

OUTPUT:
0
1
2
3

Thank you!

TRN
  • 15
  • 4
  • 1
    in the case of the list you are modifying the contents of an object that the name refers to, in case of integers you are changing what the name refers to (assigning a different object) – Matiiss Oct 21 '21 at 18:36
  • *All* values live in a single space, independent of any scopes where names that *refer* to those values reside. – chepner Oct 21 '21 at 18:41
  • 1
    I highly recommend reading this: https://nedbatchelder.com/text/names.html – jjramsey Oct 21 '21 at 18:42

3 Answers3

1

To better understand, you need to know some concepts

int is immutable and list is mutable

List of Mutable and Immutable objects Objects of built-in type that are mutable are:

  • Lists
  • Sets
  • Dictionaries
  • User-Defined Classes

Objects of built-in type that are immutable are:

  • Numbers (Integer, Rational, Float, Decimal, Complex & Booleans)
  • Strings
  • Tuples
  • Frozen Sets
  • User-Defined Classes (It purely depends upon the user to define the characteristics)

what does it mean?

for more info about immutable and mutable read this

some objects passing to function by reference and some passing by value (more)

PersianMan
  • 924
  • 1
  • 12
  • 29
  • Also it is worth to note that immutable types can be hashed by the `hash`-function and used as dict keys for example. See https://docs.python.org/3/library/stdtypes.html#immutable-sequence-types – Tom Oct 21 '21 at 18:49
  • Mutable types can also be hashed, as long as only immutable part of them are used to compute the hash. – chepner Oct 21 '21 at 18:51
  • @Tom Thanks, I learned a good point from you. – PersianMan Oct 21 '21 at 18:53
0

Variables are local if there is an assignment within a function, otherwise they are global:

 i += 1

is an assignment (i = i + 1)! Therefore i is a local variable within the function.

list_obj.append(n)

is a mutation! Therefore list_obj is looked up in the enclosing namespace which is the global one.

Side note: += can be a mutation for mutable types: list_obj += [n] would be a mutation as well.

user2390182
  • 72,016
  • 6
  • 67
  • 89
  • Note, `+=` is *always* an assignment. It's just a mutation *as well* when operating on mutable types. It's usually not important, but it comes up when you use `+=` inside a function when the left side isn't intended to be a local (it won't read from the global and assign to a local, it just dies with `UnboundLocalError`), or when you use it on a `list` inside an immutable type, e.g. `mylists = ([], []); mylists[0] += [1,2,3]` will explode (the mutation will have occurred, but the attempt to reassign `mylists[0]` will die; only `.extend` works in that case). – ShadowRanger Oct 21 '21 at 18:52
0

Objects in Python can be modified from any scope. The thing about integers is they are immutable objects so whenever they are modified they make a new copy of themselves which will only exist within the scope it is created.

Lists on the other hand are like most other Python objects and will be modified in place allowing for changes to be made regardless of the current scope.

Here is an example using a custom object.

class HoldInt:
  def __init__(self, integer):
    self.integer = integer

int_obj = HoldInt(0)

def modify_int():
  int_obj.integer += 1

print(int_obj.integer)
modify_int()
print(int_obj.integer)
0
1
Leon
  • 251
  • 2
  • 6
  • More precisely, you *can't* modify an `int`, so there's no need to implement `int.__iadd__`. When `x.__iadd__` doesn't exist, `x += y` desugars to `x = x.__add__(y)` instead of `x = x.__iadd__(y)`. – chepner Oct 21 '21 at 18:46
  • Thanks! I like this answer because it combines the excellent points about immutable objects and the scope implications made separately by @PersianMan and user2390182. I wish I could add checks to several answers! – TRN Oct 21 '21 at 19:03