#!/usr/bin/python3 # -*- coding: utf-8 -*- """ Aliasing, mutability, references """ import sys from typing import List, Tuple # (is vs. ==) is checks if two variables refer to the same object, # but == checks if the objects pointed to have the same values. a: List[int] = [1, 2, 3, 4] # Point a at a new list, [1, 2, 3, 4] b: List[int] = a # Point b at what a is pointing to print(b is a) # => True, a and b refer to the same object print(b == a) # => True, a's and b's objects are equal b = [1, 2, 3, 4] # Now, point b at a new list, [1, 2, 3, 4] print(b is a) # => False, a and b do not refer to the same object print(b == a) # => True, a's and b's objects are equal """ Identity operators is Evaluates to true if the variables on either side of the operator point to the same object and false otherwise. x is y, here is results in 1 if id(x) equals id(y). is not Evaluates to false if the variables on either side of the operator point to the same object and true otherwise. x is not y, here is not results in 1 if id(x) is not equal to id(y). """ # lists, dictionaries, and sets are mutable! x: List[int] = [1, 2, 3] # x is mutable y: List[int] = x # x and y now both "point to" (are references to) [1,2,3] print(id(x)) print(id(y)) print(x == y) print(x is y) x[2] = 5 # the mutable object pointed to by x now print(y[2]) # this means y[2] changes to 5 too! # copy method 1 y = x[:] # makes a copy print(id(x)) print(id(y)) print(x == y) print(x is y) # copy method 2 y = x.copy() print(id(x)) print(id(y)) print(x == y) print(x is y) # copy method 3 y = list(x) print(id(x)) print(id(y)) print(x == y) print(x is y) # bools, numbers, strings, tuples, and frozensets are not mutable!! z: int = 3 g: int = z z = 4 print(g) g = 7 """ Immutable types: bool int float tuple frozenset str Mutable types: list set dict """ xuniq: Tuple[int, int, int] = (1, 2, 3) # xuniq is NOT mutable yuniq: Tuple[int, int, int] = xuniq # x and y now both "point to" (are references to) [1,2,3] print(id(xuniq)) print(id(yuniq)) print(xuniq == yuniq) print(xuniq is yuniq) # We can't modify either, # but we can re-assign yuniq, # and it does not modify xuinque yuniq = (1, 2, 3) print(xuniq == yuniq) print(xuniq is yuniq) # With a tuple, this actually throws a mypy type error yuniq = (1, 2, 3, 4) print(xuniq == yuniq) print(xuniq is yuniq) # This is all perfectly consistent: # assignment can enable multiple reference, on mutable or immutable types. # re-assignment can re-assign a mutable or immutable variable to a new object. # Modification can modify mutable objects, which may be multiply referenced # Immutable objects can't be modified, regardless of multiple-reference # When we get to functions next, this will all be relevant! # Garbage collection help(sys.getrefcount) list1: List[int] = [1, 2, 3] print(sys.getrefcount(list1)) # list1's object is garbage collected now: del list1 # re-make a new object list1 = [1, 2, 3] list2: List[int] = list1 print(sys.getrefcount(list1)) # list1's object is NOT garbage collected del list1 print(sys.getrefcount(list2)) # NOTE! np/numpy arrays seemingly contradicts this array-copying design, # of when it defaults to copy versus reference when using assignment = import numpy as np help(np.array)