Dictionaries
61A Lecture 13
{'Dem': 0}
Wednesday, September 28
2
Limitations on Dictionaries
Implementing Dictionaries
def make_dict():
"""Return a functional implementation of a dictionary."""
records = []
Dictionaries are unordered collections of key-value pairs.
Dictionaries do have two restrictions:
def getitem(key):
for k, v in records:
if k == key:
return v
• A key of a dictionary cannot be an object of a mutable
def setitem(key, value):
built-in type.
• Two keys cannot be equal. There can be at most one value for
a given key.
Question: Do we need a
statement here?
nonlocal
for item in records:
if item[0] == key:
item[1] = value
return
records.append([key, value])
def dispatch(message, key=None, value=None):
if message == 'getitem':
return getitem(key)
elif message == 'setitem':
setitem(key, value)
elif message == 'keys':
return tuple(k for k, _ in records)
elif message == 'values':
return tuple(v for _, v in records)
This first restriction is tied to Python's underlying
implementation of dictionaries.
The second restriction is an intentional consequence of the
dictionary abstraction.
Demo
return dispatch
3
Message Passing
4
Dispatch Dictionaries
An approach to organizing the relationship among different
pieces of a program
Enumerating different messages in a conditional statement
isn't very convenient:
! Equality tests are repetitive
Different objects pass messages to each other
! We can't add new messages without writing new code
• What is your fourth element?
• Change your third element to this new value. (please)
A dispatch dictionary has messages as keys and functions (or
data objects) as values.
Encapsulates the behavior of all
operations on a piece of data
within one function that responds
to different messages.
Dictionaries handle the message look-up logic; we concentrate
on implementing useful behavior.
Important historical interest: the
message passing approach strongly
influenced object-oriented
programming (next lecture).
Demo
In Javascript, all objects are just dictionaries
5
6
Example: Constraint Programming
a + b = c
A Constraint Network for Temperature Conversion
Combination idea: All intermediate quantities have values too.
p * v = n * k * t
a = c - b
u
9 * c = 5 * (f - 32)
b = c - a
u
v
Both sides equal:
they must be
the same quantity
9 * celsius = 5 * (fahrenheit - 32)
Algebraic equations are declarative. They describe how
different quantities relate to one another.
This quantity
relates directly
to celsius
Python functions are procedural. They describe how to compute
a particular result from a particular set of inputs.
Constraint programming:
! We define the relationship between quantities
! We provide values for the "known" quantities
! The system computes values for the "unknown" quantities
celsius
a
w
b
9
*
This quantity
relates directly
to fahrenheit
c
u
c
*
v
a
b
x
5
a
b
y
+
c
fahrenheit
32
Challenge: We want a general means of combination.
7
Anatomy of a Constraint
Constructing a Constraint Network
celsius
a
w
b
Blue names are
"connectors"
8
*
c
u
def make_converter(celsius, fahrenheit):
"""Make a temperature conversion network."""
u, v, w, x, y = [make_connector() for _ in range(5)]
13_constraints.py
multiplier(celsius, w, u)
>>>
account['withdraw'](10)
multiplier(v,
x, u)
90adder(v, y, fahrenheit)
>>> account['deposit'](100)
13_constraints.py
constant(w, 9)
190
13_constraints.py
constant(x,
5)
>>>
account['balance']
>>>
account['withdraw'](10)
190
32)
90constant(y,
>>>
account['withdraw'](10)
Constraints compute
values for "unknown"
connectors
Boxes are
"constraints"
• Connectors represent quantities that have values.
• Constraints spread information among connectors.
• A constraint can receive two messages from its connectors:
! 'new_val' indicates that some connector that is connected
to the constraint has a new value.
! 'forget' indicates that some connector that is connected
to the constraint has forgotten its value.
9
The Messages of a Connector
connector = make_connector('Celsius')
connector['set_val'](source, value) indicates that the
source is requesting the connector to set its current
value to value.
connector['has_val']() returns whether the connector
already has a value.
connector['val'] is the current value of the connector.
connector['forget'](source) tells the connector that the
source is requesting it to forget its value.
connector['connect'](source) tells the connector to
participate in a new constraint, the source.
11
Pa
Pa
Pa
"""
>>> account['deposit'](100)
90
def
190 withdraw(amount):
>>>
account['deposit'](100)
amount > account['balance']: v
>>> if
account['balance']
190
areturn 'Insufficient funds'
a
a
celsius
190 account['balance']
>>>
u
account['balance']
é=
* c
c amount
*
+ c
fahrenheit
"""
190
account['balance'] b
def return
withdraw(amount):
"""
b
b
w
x
y
if amount > account['balance']:
def withdraw(amount):
def deposit(amount):
return>'Insufficient
funds'
if amount
account['balance']:
account['balance']
+=
5
32
funds'
account['balance']
é= amount
amount
9 return 'Insufficient
return
account['balance']
é= amount
return account['balance']
account['balance']
return account['balance']
account
= {'balance': balance, 'withdraw': withdraw, 'deposit': deposit}
def deposit(amount):
celsius
=account
make_connector('Celsius')
return
def account['balance']
deposit(amount):
+= amount
fahrenheit
= account['balance']
make_connector('Fahrenheit')
account['balance']
+= amount
return
return account['balance']
Demo
make_converter(celsius,
fahrenheit)
# Constraint
account =programming
{'balance': balance, 'withdraw': withdraw, 'deposit': deposit}
account
= {'balance': balance, 'withdraw': withdraw, 'deposit':10deposit}
return account
def make_converter(c,
f):
return account
"""Connect c to f with constraints to convert from Celsius to Fahrenheit."""
u, v, w, x,
y = [make_connector() for _ in range(5)]
# Constraint
programming
multiplier(c,
w, u)
# Constraint
programming
x, u)f):
def multiplier(v,
make_converter(c,
y,
f)
def adder(v,
make_converter(c,
f): constraints to convert from Celsius to Fahrenheit."""
"""Connect c to f with
constant(w,
"""Connect
f with constraintsfor
to convert
from Celsius to Fahrenheit."""
u,
v, w, x,c9)
yto
= [make_connector()
_ in range(5)]
constant(x,
5)
u,
v,
w,
x,
y
=
[make_connector()
for _ in range(5)]
multiplier(c, w, u)
constant(y,
32)w,
multiplier(c,
multiplier(v,
x, u)
u)
multiplier(v,
adder(v, y, f)x, u)
def adder_constraint(a,
b, c):
adder(v,
y, f)
constant(w,
9)
"""The
constraint
that a + b = c.
constant(w,
9)
constant(x,
5)
Implementing
constant(x,
5)
constant(y, an
32)Adder Constraint
>>>
a, b, c =32)
[make_connector(name) for name in ('a', 'b', 'c')]
constant(y,
constraint = adder_constraint(a,
b, c)
def >>>
adder_constraint(a,
b, c):
def >>>
adder_constraint(a,
b, a
c):
a['set_val']('user',
2)
"""The constraint that
+ b = c.
a"""The
= 2 constraint that a + b = c.
>>>
3)
>>> b['set_val']('user',
a, b, c = [make_connector(name)
for name in ('a', 'b', 'c')]
b, c = [make_connector(name)
name in ('a', 'b', 'c')]
b>>>
= 3a,
>>>
constraint
= adder_constraint(a,for
b, c)
constraint
=
adder_constraint(a,
b, c)
c>>>
=
5
>>> a['set_val']('user', 2)
>>>
a['set_val']('user',
2)
"""
a = 2
a
= new_value():
2
def
>>>
b['set_val']('user', 3)
>>>
b['set_val']('user',
3)
bv, cv = [connector['has_val']()
for connector in (a, b, c)]
b = av,
3
b
=
if
c = 3
5 av and bv:
c
=
5
c['set_val'](constraint,
a['val']
+ b['val'])
"""
"""
av and cv:
def elif
new_value():
def av,
new_value():
b['set_val'](constraint,
c['val']
é a['val'])
bv, cv = [connector['has_val']() for
connector in (a, b, c)]
av, bv,
cv =cv:
[connector['has_val']() for connector in (a, b, c)]
elif
bvand
and
if
av
# We
willbv:
implement this function
momentarily!
if
av
and
bv:
a['set_val'](constraint,
c['val']
é+ b['val'])
c['set_val'](constraint,
a['val']
b['val'])
c['set_val'](constraint, a['val'] + b['val'])
elif
elif av
av and
and cv:
cv:
def forget_value():
b['set_val'](constraint,
c['val'] é a['val'])
b['set_val'](constraint,
for
connector
in (a, b, c): c['val'] é a['val'])
elif
bv
elifconnector['forget'](constraint)
bv and
and cv:
cv:
a['set_val'](constraint,
a['set_val'](constraint, c['val']
c['val'] é
é b['val'])
b['val'])
constraint
= {'new_val': new_value, 'forget': forget_value}
def
forget_value():
def forget_value():
for
connector
in
(a,
b,
c):
for connector
in b,
(a,c):
b, c):
for connector
in (a,
connector['forget'](constraint)
connector['forget'](constraint)
connector['connect'](constraint)
constraint =
= {'new_val':
{'new_val': new_value,
new_value, 'forget':
'forget': forget_value}
forget_value}
constraint
return
constraint
for connector
connector in
in (a,
(a, b,
b, c):
c):
for
connector['connect'](constraint)
12
connector['connect'](constraint)
def make_ternary_constraint(a,
b, c, ab, ca, cb):
"""The
constraint
that ab(a,b)=c and ca(c,a)=b and cb(c,b) = a."""
return
constraint
return
constraint
def
new_value():
av, bv, cv = [connector['has_val']() for connector in (a, b, c)]
if av and bv:
def make_ternary_constraint(a,
make_ternary_constraint(a,
b, c,
c, ab,
ab, ca,
ca, cb):
cb):
def
b,
c['set_val'](constraint,
"""The
constraint
that ab(a,b)=c ab(a['val'],
and ca(c,a)=bb['val']))
and cb(c,b) = a."""
"""The
constraint
av and cv:that ab(a,b)=c and ca(c,a)=b and cb(c,b) = a."""
def elif
new_value():
def
new_value():
ac(c['val'],
a['val']))in (a, b, c)]
av, b['set_val'](constraint,
bv, cv = [connector['has_val']()
for connector
elif av and cv:
a['val'] + b['val'])
elifc['set_val'](constraint,
av and cv:
b['set_val'](constraint,
c['val'] é a['val'])
elifb['set_val'](constraint,
av and cv:
c['val'] é a['val'])
elif bv and cv:
c['val'] é a['val'])
elifb['set_val'](constraint,
bv
and
cv:
a['set_val'](constraint,
c['val'] é b['val'])
elifa['set_val'](constraint,
bv and cv:
c['val'] é b['val'])
a['set_val'](constraint, c['val'] é b['val'])
def forget_value():
def forget_value():
for connector in (a, b, c):
def forget_value():
for connector
in (a, b, c):
connector['forget'](constraint)
for connector['forget'](constraint)
connector
in (a, b, c):
connector['forget'](constraint)
constraint = {'new_val': new_value, 'forget': forget_value}
constraint = {'new_val': new_value, 'forget': forget_value}
constraint = {'new_val': new_value, 'forget': forget_value}
for connector in (a, b, c):
for connector
in (a, b, c):
connector['connect'](constraint)
for connector['connect'](constraint)
connector in (a, b, c):
connector['connect'](constraint)
return
constraint
Connectors
Relations
return constraint
return constraint
def make_ternary_constraint(a, b, c, ab, ca, cb):
def make_ternary_constraint(a,
b, c, ab,
cb): and cb(c,b)=a."""
"""The constraint that ab(a,b)=c
and ca,
ca(c,a)=b
def make_ternary_constraint(a,
b, c, and
ab, ca(c,a)=b
ca, cb): and cb(c,b) = a."""
"""The
constraint that ab(a,b)=c
def new_value():
"""The
constraint
that
ab(a,b)=c
and
ca(c,a)=b
and cb(c,b)
= a."""
def
new_value():
13_constraints.py
av, bv, cv = [connector['has_val']() for connector
in (a,
b, c)]
def av,
new_value():
bv,and
cv bv:
= [connector['has_val']() for connector in (a, b, c)]
if av
av,
bv,
cv
= cv:
[connector['has_val']()
for connector
in (a, b, c)]
if
av
and
bv:
elif
bv
and
c['set_val'](constraint,
ab(a['val'],
b['val']))
if
av
and
bv:
c['set_val'](constraint,
ab(a['val'],
b['val']))
cb(c['val'], b['val']))
elifa['set_val'](constraint,
av and cv:
c['set_val'](constraint,
ab(a['val'],
av and cv:
def elif
forget_value():
b['set_val'](constraint,
ac(c['val'], b['val']))
a['val']))
elif
av and cv:
for b['set_val'](constraint,
connector
in (a, b, c): ac(c['val'], a['val']))
b['set_val'](constraint,
ac(c['val'], a['val']))
13_constraints.py
connector['forget'](constraint)
constraint = {'new_val': new_value, 'forget': forget_value}
bv and
for elif
connector
incv:
(a, b, c):
a['set_val'](constraint, cb(c['val'], b['val']))
connector['connect'](constraint)
def
forget_value():
return
constraint
for connector in (a, b, c):
connector['forget'](constraint)
from operator
import add, sub, mul, truediv
constraint = {'new_val': new_value, 'forget': forget_value}
connector
in (a, b, c):
def for
adder(a,
b, c):
connector['connect'](constraint)
"""The
constraint that a + b = c."""
return
return constraint
make_ternary_constraint(a, b, c, add, sub, sub)
Generalizing to a Multiplication Constraint
from
operator import
def multiplier(a,
b, add,
c): sub, mul, truediv
"""The constraint that a * b = c."""
def adder(a,
b,
c):
return make_ternary_constraint(a, b, c, mul, truediv, truediv)
"""The constraint that a + b = c."""
make_ternary_constraint(a,
b, c, add, sub, sub)
def return
constant(connector,
value):
"""The constraint that connector = value."""
def multiplier(a,
b, c):
constraint = {}
"""The
constraint that a * b = c."""
connector['set_val'](constraint,
value)
return
b, c, mul, truediv, truediv)
return make_ternary_constraint(a,
constraint
def
value):
def constant(connector,
make_connector(name=None):
"""The
constraint
that connector
= value."""
"""A connector
between
constraints.
constraint = {}
connector['set_val'](constraint,
value)
>>> celsius = make_connector('Celsius')
return
constraint
>>> fahrenheit
= make_connector('Fahrenheit')
>>> make_converter(celsius, fahrenheit)
def make_connector(name=None):
"""A
connector between constraints.
>>> celsius['set_val']('user',
25)
Celsius = 25
>>>
celsius==77.0
make_connector('Celsius')
Fahrenheit
>>> fahrenheit = make_connector('Fahrenheit')
>>> make_converter(celsius, fahrenheit)
>>> fahrenheit['set_val']('user', 212)
Contradiction
detected: 77.0 vs25)
212
>>>
celsius['set_val']('user',
Celsius = 25
>>> celsius['forget']('user')
Fahrenheit
= 77.0
Celsius is forgotten
Fahrenheit
is forgotten
>>>
fahrenheit['set_val']('user',
212)
Contradiction detected: 77.0 vs 212
>>> fahrenheit['set_val']('user', 212)
Fahrenheit
=
212
>>> celsius['forget']('user')
Celsius is
= 100.0
Celsius
forgotten
"""
Fahrenheit
is forgotten
informant = None
constraints
= []
>>>
fahrenheit['set_val']('user',
212)
def
set_value(source,
value):
Fahrenheit = 212
nonlocal
informant
Celsius
= 100.0
val
=
connector['val']
"""
if val=is
None:
informant
None
informant,
connector['val'] = source, value
constraints
= []
if name is notvalue):
None:
def set_value(source,
nonlocalprint(name,
informant '=', value)
inform_all_except(source,
'new_val', constraints)
val = connector['val']
else:
if
val is None:
if val != value:
informant,
connector['val'] = source, value
print('Contradiction
detected:', val, 'vs', value)
if name
is not None:
def forget_value(source):
print(name, '=', value)
nonlocal
informant
inform_all_except(source, 'new_val', constraints)
if informant == source:
else:
informant,
connector['val'] = None, None
if
val != value:
if name
is not None:
print('Contradiction
detected:', val, 'vs', value)
print(name, 'is forgotten')
def forget_value(source):
inform_all_except(source,
'forget', constraints)
nonlocal
informant
connector
= {'val':
None,
if informant
== source:
'set_val':
set_value, = None, None
informant,
connector['val']
'forget':
forget_value,
if name
is not None:
'has_val': 'is
lambda:
connector['val'] is not None,
print(name,
forgotten')
'connect': lambda source:
constraints.append(source)}
inform_all_except(source,
'forget',
constraints)
connector = {'val': None,
'set_val': set_value,
'forget': forget_value,
'has_val': lambda: connector['val'] is not None,
'connect': lambda source: constraints.append(source)}
13
>>> make_converter(celsius,
fahrenheit = make_connector('Fahrenheit')
>>>
fahrenheit)
fahrenheit
make_converter(celsius,
fahrenheit)
def >>>
adder(a,
b, c):= make_connector('Fahrenheit')
>>>
celsius['set_val']('user',
make_converter(celsius,
"""The
constraint that a + bfahrenheit)
= 25)
c."""
>>>
celsius['set_val']('user',
25)
Celsius
=
25
>>>
celsius['set_val']('user',
25) b, c, add, sub, sub)
return make_ternary_constraint(a,
Celsius
= 25
Fahrenheit
=
77.0
>>>
celsius['set_val']('user',
25)
Celsius
= 25
Fahrenheit
= 77.0
Celsius
= 25
= 77.0
def Fahrenheit
multiplier(a,
b, c):
>>>
fahrenheit['set_val']('user',
212)
Fahrenheit
= 77.0 that a * b = c."""
"""The
constraint
>>>
fahrenheit['set_val']('user',
212)
Contradiction
detected: 77.0 vs 212
>>>
fahrenheit['set_val']('user',
212)
return
make_ternary_constraint(a,
b, c, mul, truediv, truediv)
Contradiction
detected: 77.0 vs 212
>>>
fahrenheit['set_val']('user',
212)
Contradiction
detected: 77.0 vs 212
celsius['forget']('user')
Contradiction
detected:
77.0
vs
212
def >>>
constant(connector,
value):
>>>
celsius['forget']('user')
Celsius
is
forgotten
"""The
constraint
that
connector
=
value."""
>>>
celsius['forget']('user')
Celsius
is is
forgotten
Fahrenheit
forgotten
>>>
celsius['forget']('user')
constraint
= {}
Celsius
is is
forgotten
Fahrenheit
forgotten
connector['set_val'](constraint,
value)
Celsius
is is
forgotten
Fahrenheit
forgotten
>>>
fahrenheit['set_val']('user',
212)
return
constraint
Fahrenheit
is forgotten
>>>
fahrenheit['set_val']('user',
212)
Fahrenheit
= 212
>>>
fahrenheit['set_val']('user',
212)
Fahrenheit
= 212
Celsius
= 100.0
def >>>
make_connector(name=None):
fahrenheit['set_val']('user',
212)
Fahrenheit
= 212
Celsius
=
100.0
"""
"""A connector
between constraints.
Fahrenheit
=
212
Celsius
=
100.0
"""
informant
=
None
Celsius
=
100.0
"""
informant
= =
None
constraints
[]
>>>
celsius
make_connector('Celsius')
"""
informant
= =
None
constraints
[]
def
value):
>>> set_value(source,
fahrenheit
= make_connector('Fahrenheit')
informant
= =
None
constraints
[]
def
set_value(source,
value):
nonlocal
>>> set_value(source,
make_converter(celsius,
fahrenheit)
constraints
=informant
[]
def
value):
nonlocal
informant
val
=
connector['val']
def val
set_value(source,
nonlocal
informantvalue):
=
connector['val']
if
val
is
None:
>>> val
celsius['set_val']('user',
25)
Page 3
nonlocal
informant
= connector['val']
if
val
is
None:
informant,
= source, value
Celsius
= connector['val']
25
val
=
if val
is
None:connector['val']
informant,
= source, value
Fahrenheit
= 77.0
if name
is connector['val']
not None:
if val
is
None:
informant,
= source, value
if name
is connector['val']
not None:
print(name,
'=',
value)
informant,
connector['val']
= source, value
if
name
is
not
None:
print(name,
'=',
value)
>>> fahrenheit['set_val']('user',
212)
inform_all_except(source,
'new_val',
constraints)
if
name
is
not
None:
print(name,
'=',
value)
inform_all_except(source,
'new_val', constraints)
Contradiction
detected: 77.0 vs 212
else:
print(name,
'=',
value)
inform_all_except(source,
'new_val', constraints)
else:
if
val
!=
value:
inform_all_except(source,
'new_val', constraints)
Page 3
if val
!= value:
>>> else:
celsius['forget']('user')
print('Contradiction
detected:',
val, 'vs', value)
else:
if
!= value:
print('Contradiction
detected:', val, 'vs', value)
Celsius
is val
forgotten
def
forget_value(source):
if val
!= value:
print('Contradiction
detected:', val, 'vs', value)
def
forget_value(source):
Fahrenheit
is
forgotten
nonlocal
informant
print('Contradiction
detected:', val, 'vs', value)
def if
forget_value(source):
nonlocal
informant
informant
== source:
def
forget_value(source):
nonlocal
informant
>>> if
fahrenheit['set_val']('user',
212)
informant
==connector['val']
source:
informant,
= None, None
nonlocal
if
informant
==connector['val']
source:
Fahrenheit
= informant
212is
informant,
= None, None
if name
not
None:
if informant
==connector['val']
source:
informant,
= None, None
Celsius
= 100.0
if
name
is
not
None:
print(name,
'is forgotten')
informant,
= None, constraints)
None
if name
is connector['val']
not None:
"""
print(name,
'is forgotten')
inform_all_except(source,
'forget',
if
name
is
not
None:
print(name,
'is
forgotten')
informant
=
None
inform_all_except(source,
'forget', constraints)
connector
= {'val': None,
print(name,
'is
forgotten')
inform_all_except(source,
constraints
='set_val':
[]
connector
= {'val':
None,
set_value,'forget', constraints)
inform_all_except(source,
connector
= {'val':
None,
def set_value(source,
value):
'set_val':
set_value,'forget', constraints)
'forget':
forget_value,
connector
= {'val':
None,
'set_val':
set_value,
nonlocal
informant
'forget':
forget_value,
'has_val':
lambda: connector['val'] is not None,
'set_val':
set_value,
val = connector['val']
'forget':
'has_val':
lambda:
connector['val']
is not None,
'connect':forget_value,
lambda source:
constraints.append(source)}
if val is'forget':
None:
forget_value,
'has_val':
lambda:
connector['val']
is not None,
'connect':
lambda source:
constraints.append(source)}
informant,
connector['val']
= source,
value
'has_val':
lambda:
connector['val']
is not None,
'connect':
lambda source:
constraints.append(source)}
13_constraints.py
if name
is not None:
'connect':
lambda source: constraints.append(source)}
print(name, '=', value)
return connector
inform_all_except(source, 'new_val', constraints)
else:
def inform_all_except(source,
if val != value: message, constraints):
"""Inform allprint('Contradiction
constraints of the message,
except
detected:',
val,source."""
'vs', value)
for
in constraints:
def c
forget_value(source):
if
c != source:
nonlocal
informant
c[message]()
if informant
== source:
informant, connector['val'] = None, None
@main
if name is not None:
def run():
print(name, 'is forgotten')
celsius =inform_all_except(source,
make_connector('Celsius')
'forget', constraints)
fahrenheit
make_connector('Fahrenheit')
connector == {'val':
None,
make_converter(celsius,
'set_val': fahrenheit)
set_value,
interact()
'forget': forget_value,
'has_val': lambda: connector['val'] is not None,
'connect': lambda source: constraints.append(source)}
Implementing a Connector
Page 4
14
© Copyright 2026 Paperzz