Value types and reference types¶
- C++ and others primarily use value types.
- Variables are named memory locations (boxes) that hold values.
- Each variable is its own named box and holds its own value.
- Python variables are different than in many languages.
- For convenience, we can think of Python as using reference types.
- The object is what is special.
- Variable names are like tags bound to a given object.
- The
idfunction in Python shows the memory location of a given object.
In [24]:
x = 5.5
y = x
print(f"Location of x is {id(x)}")
print(f"Location of y is {id(y)}, the same as x")
Location of x is 13418444784 Location of y is 13418444784, the same as x
- Given variables
xandy, if we now set y=6.6, we create a new object, 6.6, and tag it with the variable namedy
In [25]:
y = 6.6
print(f"Location of y is {id(y)}, now different than x above")
Location of y is 13420071632, now different than x above
- For simple variables and assignments like this the difference is hardly noticeable and we can practically ignore it (as we have been).
- This is because we have mostly been working with immutable objects.
Mutable and immutable objects¶
- Python is object-centric.
- Objects come in two flavors:
- immutable (not changeable),
- numbers, strings, booleans, tuples, etc.
- mutable (changeable).
- lists, dictionaries, numpy arrays, etc.
- immutable (not changeable),
In [26]:
import numpy as np
a = np.array([1,2,3,4])
b = a # try b = a.copy() instead
a[0] = 101325
print(b)
id(a), id(b)
[101325 2 3 4]
Out[26]:
$\displaystyle \left( 13421333744, \ 13421333744\right)$
- Variables
aandbboth tag the same list object. - If we change that object through
a[0] = 101325, thenbappears to have also changed. - This seems strange and confuses beginners.
- But it makes sense when we realize that
aandbboth tag the same object.- So, if the underlying object changes, that will be reflected in both
a[0]andb[0].
- So, if the underlying object changes, that will be reflected in both
- If you want
aandbto refer to separate objects so that settinga[0]=101325won't affectb, the useb=a.copy()
In [27]:
a = [1,2,3,4]
print(f"id(a) = {id(a)}")
b = a.copy()
print(f"id(b) = {id(b)}, is different than a")
a[0] = 101325
print(f"setting a[0]= 101325")
print(f"b[0] hasn't changed: {b[0]}")
id(a) = 13690545280 id(b) = 13690537920, is different than a setting a[0]= 101325 b[0] hasn't changed: 1
In [28]:
a = np.array([1,2,3,4])
b = a*2 # assigns b to a new object array a*2; a is unchanged
a[0] = 101325 # b is unchanged
b
id(a), id(b)
Out[28]:
$\displaystyle \left( 13421333552, \ 13421334800\right)$
In [29]:
def f(x):
return "kittens"
print = f
print("101325")
del(print) # to reset print
Function arguments and scope¶
- Variable names (tags) are bound to objects.
- Variables can be seen and used in enclosing scopes.
- Functions can see variables defined in the global scope.
- A function cannot rebind a global variable to a different object.
- But it can make a different variable with the same name and bind that to a new object.
- (in a lower scope)
- That is: same name, different variable, bound to a different object.
- The global scope variable-object binding are unchanged.
- But it can make a different variable with the same name and bind that to a new object.
Example 1¶
- The same variable name can be used to tag different objects in different scopes.
- The variables are different, but the names are the same.
- Think of two different people with the same name.
- In the following example, outside the function in the global scope we create an immutable object
2(an integer) and tag it with variablex.xhere is a global variable.
- We call the function
f(x)- Then
x=6creates a new object6, and tags it with the local variablex.- Same name, different object, different scope
- That is, we are not allowed to change the global tag-object binding
xto2, so we create a localxtag that is bound to the object6.
- Then
- The outside object
2still exists and is still tagged by the global variablex.
In [ ]:
x = 2 # 1. create integer object 2 tagged by x
#-----------------------------------
def f(x):
print("x top of func:", x, id(x))
x = 6 # 2A. create LOCAL tag x bound to different object 6
print("x end of func:", x, id(x))
#-----------------------------------
f(x) # 2. call the function
print("x final: ", x, id(x)) # 3. outer object x is unchanged
x top of func: 2 4300629376 x end of func: 6 4300629504 x final: 2 4300629376
Example 2¶
- Create the mutable list object tagged by variable
myx. - Pass this to the function.
- The function tags this same object with local variable
x. - The function then changes an element of this list object.
- That object is still changed outside the function, because it is the same object.
- The list container itself is the object, not what it holds.
So, you can change mutable objects through the function argument list, without using a return statement.
In [31]:
def f(x): # 3. same object tagged with variable name x
x[1] = 6 # 4. change value of element in same object
#---------------------
myx = [2,2] # 1. define list object tagged by variable name myx
f(myx) # 2. call function: changes object, prints address
print(myx) # 5. print values: list is still changed outside
[2, 6]