Dictionaries, Keys, Membership, and Iteration

Key Takeaways

  • Dictionaries map unique keys to values, so each key can appear only once in a dictionary object.
  • Dictionary keys must be hashable, which allows strings, numbers, booleans, and tuples of hashable values but rejects lists and sets.
  • If a dictionary literal repeats a key, the later value overwrites the earlier value for that same key.
  • The in operator checks dictionary keys, not values, unless you explicitly test values().
  • Iterating directly over a dictionary produces keys; use items() when both keys and values are needed.
Last updated: June 2026

Mapping instead of position

A dictionary stores associations between keys and values, written with curly braces and key: value pairs. Lists, tuples, and strings are indexed by integer position; dictionaries are looked up by key. There is no dict[0] meaning "first item" unless 0 happens to be a key.

scores = {'Ana': 91, 'Bo': 84}
print(scores['Ana'])  # 91

Keys are unique inside one dictionary. If code assigns a value to an existing key, the old value is replaced; the dictionary never keeps two values for the same key.

scores = {'Ana': 91, 'Ana': 95}
print(scores)  # {'Ana': 95}

The same overwrite behavior appears when assigning after creation, and you can add brand-new keys the same way:

scores['Ana'] = 100   # overwrite existing key
scores['Cy'] = 77     # add a new key

Since Python 3.7, dictionaries preserve insertion order, so iteration follows the order keys were first added.

Hashable keys

Dictionary lookup depends on hashing, so keys must be hashable. Immutable built-in values such as strings, integers, floats, booleans, and tuples of hashable elements can be keys. Mutable collections such as lists, dictionaries, and sets cannot.

Candidate keyValid key?Reason
'id'YesStrings are hashable
42YesIntegers are hashable
3.14YesFloats are hashable
TrueYesBooleans are hashable
(2026, 'PCEP')YesTuple of hashable elements
[2026, 'PCEP']NoLists are mutable and unhashable
{'x', 'y'}NoSets are mutable and unhashable

A tuple is not automatically safe. If it contains a list, the tuple is unhashable because one element is unhashable.

valid = {(1, 2): 'point'}
invalid = {([1, 2], 3): 'bad'}  # TypeError: unhashable type: 'list'

One subtle trap: because True == 1 and False == 0, using True and 1 as separate keys collides. {1: 'a', True: 'b'} produces {1: 'b'} because they hash equal.

Lookup, get, and membership

Using square brackets with a missing key raises KeyError.

profile = {'name': 'Mina'}
print(profile['age'])  # KeyError

get() is safer when a missing key is acceptable. It returns None by default, or a fallback value you supply, and never raises KeyError.

print(profile.get('age'))       # None
print(profile.get('age', 0))    # 0

Membership with in checks keys, not values.

profile = {'name': 'Mina', 'role': 'student'}
print('name' in profile)            # True
print('Mina' in profile)            # False
print('Mina' in profile.values())   # True

This is heavily tested because beginners read a dictionary as a bag of all contained data. Python treats direct membership as key membership.

Iteration views

Iterating over a dictionary directly yields keys.

for key in profile:
    print(key)

Use view methods when you need a specific perspective:

MethodProvides
keys()The keys
values()The values
items()Key-value pairs as tuples
for key, value in profile.items():
    print(key, value)

Mutating methods

Dictionaries are mutable: you can add, replace, and delete entries. Common methods include update(other) (merge in pairs), pop(key) (remove and return the value, KeyError if absent unless a default is given), popitem() (remove and return the last inserted pair), setdefault(key, default), and clear(). As with lists, update() and clear() return None, while pop(key) returns the removed value.

For PCEP, always trace whether the code is reading a value, mutating the mapping, or testing only keys, and notice when a method changes the dictionary before a later line reads it; the final answer often depends on the latest value bound to a key.

Building dictionaries and counting patterns

The PCEP exam tests several ways a dictionary is created and updated, and the most common scenario is accumulating counts or grouping data. A literal {'a': 1, 'b': 2} is the simplest form, but the dict() constructor also builds one from keyword arguments (dict(a=1, b=2)) or from an iterable of pairs (dict([('a', 1), ('b', 2)])). The setdefault(key, default) method is a frequent exam target: it returns the existing value if the key is present, otherwise it inserts the key with the default and returns that default, all in one step. This makes it ideal for counting without a KeyError.

For example, looping over 'banana' and writing counts[ch] = counts.get(ch, 0) + 1 builds {'b': 1, 'a': 3, 'n': 2}, where get supplies a starting value of 0 the first time each character is seen. The update() method merges another dictionary or iterable of pairs into the current one, overwriting any shared keys with the incoming values, and returns None. When tracing such code, track each key's current value line by line and remember that re-assigning an existing key never grows the dictionary; it only replaces the stored value.

Also recall that len(d) counts keys, not values, and that iterating with for k in d walks the keys in insertion order, which lets you predict printed output exactly when the snippet builds the dictionary step by step before looping over it.

Test Your Knowledge

What is printed by this code? d = {'x': 1, 'x': 2}; print(d['x'])

A
B
C
D
Test Your Knowledge

For d = {'a': 10, 'b': 20}, what does 'a' in d test?

A
B
C
D
Test Your Knowledge

Which attempted dictionary key raises TypeError?

A
B
C
D