Variables Are Not Boxes


In [2]:
a = [1, 2, 3]
b = a
a.append(4)
b


Out[2]:
[1, 2, 3, 4]

In [3]:
class Gizmo:
    def __init__(self):
        print('Gizmo id: %d' % id(self))

In [4]:
x = Gizmo()


Gizmo id: 1095113832488

In [5]:
y = Gizmo() * 10


Gizmo id: 1095113832376
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-0f524f080953> in <module>()
----> 1 y = Gizmo() * 10

TypeError: unsupported operand type(s) for *: 'Gizmo' and 'int'

Identity, Equality, and Aliases


In [6]:
charles = {'name': 'Charles L. Dodgson', 'born': 1832}

In [7]:
lewis = charles

In [8]:
lewis is charles


Out[8]:
True

In [9]:
id(charles), id(lewis)


Out[9]:
(1095096406280, 1095096406280)

In [10]:
lewis['balance'] = 950

In [11]:
charles


Out[11]:
{'balance': 950, 'born': 1832, 'name': 'Charles L. Dodgson'}

In [12]:
alex = {'name': 'Charles L. Dodgson', 'born': 1832, 'balance':950}

In [13]:
alex == charles


Out[13]:
True

In [15]:
alex is not charles


Out[15]:
True

The Relative Immutability of Tuples


In [16]:
t1 = (1, 2, [30, 40])
t2 = (1, 2, [30, 40])
t1 == t2


Out[16]:
True

In [17]:
id(t1[-1])


Out[17]:
1095111699976

In [18]:
t1[-1].append(99)

In [19]:
t1


Out[19]:
(1, 2, [30, 40, 99])

In [20]:
id(t1[-1])


Out[20]:
1095111699976

In [21]:
t1 == t2


Out[21]:
False

Copies Are Shallow by Default


In [22]:
l1 = [3, [55, 44], (7, 8, 9)]
l2 = list(l1)
l2


Out[22]:
[3, [55, 44], (7, 8, 9)]

In [23]:
l2 == l1


Out[23]:
True

In [24]:
l2 is l1


Out[24]:
False

In [25]:
l1 = [3, [66, 55, 44], (7, 8, 9)]
l2 = list(l1)
l1.append(100)
l1[1].remove(55)
print('l1:', l1)
print('l2:', l2)
l2[1] += [33, 22]
l2[2] += (10,11)
print('l1:', l1)
print('l2:', l2)


l1: [3, [66, 44], (7, 8, 9), 100]
l2: [3, [66, 44], (7, 8, 9)]
l1: [3, [66, 44, 33, 22], (7, 8, 9), 100]
l2: [3, [66, 44, 33, 22], (7, 8, 9, 10, 11)]

Deep and Shallow Copies of Arbitrary Objects


In [26]:
class Bus:
    
    def __init__(self, passengers=None):
        if passengers is None:
            self.passengers = []
        else:
            self.passengers = list(passengers)
            
    def pick(self, name):
        self.passengers.append(name)
        
    def drop(self, name):
        self.passengers.remove(name)

In [27]:
import copy

In [34]:
bus1 = Bus(['Alice','Bill', 'Claire', 'David'])
bus2 = copy.copy(bus1)
bus3 = copy.deepcopy(bus1)
id(bus1), id(bus2), id(bus3)


Out[34]:
(1095114522184, 1095114522128, 1095114522296)

In [35]:
bus1.drop('Bill')
bus2.passengers


Out[35]:
['Alice', 'Claire', 'David']

In [37]:
id(bus1.passengers), id(bus2.passengers), id(bus3.passengers)


Out[37]:
(1095114547784, 1095114547784, 1095114476232)

In [38]:
bus3.passengers


Out[38]:
['Alice', 'Bill', 'Claire', 'David']

In [39]:
a = [10, 20]
b = [a, 30]
a.append(b)
a


Out[39]:
[10, 20, [[...], 30]]

In [40]:
from copy import deepcopy
c = deepcopy(a)
c


Out[40]:
[10, 20, [[...], 30]]

Function Parameters as References


In [41]:
def f(a, b):
    a += b
    return a

In [42]:
x = 1
y = 2
f(x, y)


Out[42]:
3

In [43]:
x, y


Out[43]:
(1, 2)

In [44]:
x = [1, 2]
y = [3, 4]
f(x, y)


Out[44]:
[1, 2, 3, 4]

In [45]:
x, y


Out[45]:
([1, 2, 3, 4], [3, 4])

In [46]:
t = (10, 20)
u = (30, 40)
f(t, u)


Out[46]:
(10, 20, 30, 40)

In [47]:
t, u


Out[47]:
((10, 20), (30, 40))

Mutable Types as Parameter Defaults: Bad Idea


In [48]:
class HauntedBus(Bus):
    """A bus model haunted by ghost passengers"""
    
    def __init__(self, passengers=[]):
        self.passengers = passengers

In [49]:
bus1 = HauntedBus(['Alice', 'Bill'])
bus1.passengers


Out[49]:
['Alice', 'Bill']

In [50]:
bus1.pick('Charlie')
bus1.drop('Alice')
bus1.passengers


Out[50]:
['Bill', 'Charlie']

In [51]:
bus2 = HauntedBus()
bus2.pick('Carrie')
bus2.passengers


Out[51]:
['Carrie']

In [52]:
bus3 = HauntedBus()
bus3.passengers


Out[52]:
['Carrie']

In [53]:
bus3.pick('Dave')
bus2.passengers


Out[53]:
['Carrie', 'Dave']

In [54]:
bus2.passengers is bus3.passengers


Out[54]:
True

In [55]:
bus1.passengers


Out[55]:
['Bill', 'Charlie']

In [57]:
print(dir(HauntedBus.__init__))


['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

In [58]:
HauntedBus.__init__.__defaults__


Out[58]:
(['Carrie', 'Dave'],)

In [59]:
HauntedBus.__init__.__defaults__[0] is bus2.passengers


Out[59]:
True

Defensive Programming with Mutable Parameters


In [60]:
class Twilightbus(Bus):
    """A bus model that makes passengers vanish"""
    
    def __init__(self, passengers=None):
        if passengers is None:
            self.passengers = []
        else:
            self.passengers = passengers

In [61]:
basketball_team = ['Sue', 'Tina', 'Maya', 'Diana', 'Pat']
bus = Twilightbus(basketball_team)
bus.drop('Tina')
bus.drop('Pat')
basketball_team


Out[61]:
['Sue', 'Maya', 'Diana']

del and Garbage Collection


In [1]:
import weakref

In [2]:
s1 = {1, 2, 3}
s2 = s1
def bye():
    print('Gone with the wind...')
ender = weakref.finalize(s1, bye)
ender.alive


Out[2]:
True

In [3]:
del s1

In [4]:
ender.alive


Out[4]:
True

In [5]:
s2 = 'spam'


Gone with the wind...

In [6]:
ender.alive


Out[6]:
False

Weak References


In [7]:
import weakref

In [8]:
a_set = {0, 1}
wref = weakref.ref(a_set)
wref


Out[8]:
<weakref at 0x000000CA5EF6DD68; to 'set' at 0x000000CA5DBA6748>

In [9]:
wref()


Out[9]:
{0, 1}

In [10]:
a_set = {2, 3, 4}

In [11]:
wref()


Out[11]:
{0, 1}

In [12]:
wref() is None


Out[12]:
False

In [13]:
wref() is None


Out[13]:
False

The WeakValueDictionary Skit


In [17]:
class Cheese:
    
    def __init__(self, kind):
        self.kind = kind
        
    def __repr__(self):
        return 'Cheese(%r)' % self.kind

In [18]:
import weakref
stock = weakref.WeakValueDictionary()
catalog = [Cheese('Red Leicester'), Cheese('Tilsit'), Cheese('Brie'),
           Cheese('Parmesan')]

In [19]:
for cheese in catalog:
    stock[cheese.kind] = cheese
sorted(stock.keys())


Out[19]:
['Brie', 'Parmesan', 'Red Leicester', 'Tilsit']

In [20]:
del catalog

In [21]:
sorted(stock.keys())


Out[21]:
['Parmesan']

In [22]:
del cheese

In [23]:
sorted(stock.keys())


Out[23]:
[]

Limitations of Weak References


In [24]:
class MyList(list):
    """list subclass whose instances may be weakly referenced"""
    
a_list = MyList(range(10))

In [25]:
wref_to_a_list = weakref.ref(a_list)

In [26]:
wref_to_a_list()


Out[26]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [27]:
del a_list

In [28]:
wref_to_a_list()


Out[28]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [30]:
wref_to_a_list is None


Out[30]:
False

In [31]:
wref_to_a_list()


Out[31]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Tricks Python Plays with Immutables


In [32]:
t1 = (1, 2, 3)
t2 = tuple(t1)
t2 is t1


Out[32]:
True

In [34]:
t3 = t1[:]
t3 is t1


Out[34]:
True

In [35]:
t1 = (1, 2, 3)
t3 = (1, 2, 3)
t3 is t1


Out[35]:
False

In [36]:
s1 = 'ABC'
s2 = 'ABC'
s2 is s1


Out[36]:
True

In [ ]: