Python - Dictionary fromkeys() Method

• The `fromkeys()` method creates a new dictionary with specified keys and a single default value, useful for initializing dictionaries with predetermined structure

Key Insights

• The fromkeys() method creates a new dictionary with specified keys and a single default value, useful for initializing dictionaries with predetermined structure • All keys share the same value reference—using mutable defaults like lists or dictionaries creates a shared reference trap that causes unexpected behavior • Common use cases include creating lookup tables, initializing counters, and setting up configuration dictionaries with default values

Understanding fromkeys() Syntax

The fromkeys() method is a class method of the dictionary type that creates a new dictionary from a sequence of keys. The syntax is straightforward:

dict.fromkeys(keys, value=None)

The method accepts two parameters: keys (an iterable of keys) and an optional value (the default value for all keys). If no value is specified, all keys map to None.

# Basic usage
keys = ['name', 'age', 'city']
user = dict.fromkeys(keys)
print(user)
# Output: {'name': None, 'age': None, 'city': None}

# With a default value
config = dict.fromkeys(['debug', 'verbose', 'logging'], False)
print(config)
# Output: {'debug': False, 'verbose': False, 'logging': False}

Creating Dictionaries from Various Iterables

The fromkeys() method accepts any iterable as the keys parameter, not just lists. This flexibility allows you to create dictionaries from strings, tuples, sets, or even ranges.

# From a string
char_dict = dict.fromkeys('hello', 0)
print(char_dict)
# Output: {'h': 0, 'e': 0, 'l': 0, 'o': 0}

# From a tuple
status_codes = dict.fromkeys((200, 404, 500), 'HTTP Status')
print(status_codes)
# Output: {200: 'HTTP Status', 404: 'HTTP Status', 500: 'HTTP Status'}

# From a range
counters = dict.fromkeys(range(1, 6), 0)
print(counters)
# Output: {1: 0, 2: 0, 3: 0, 4: 0, 5: 0}

# From a set (note: order may vary)
unique_keys = dict.fromkeys({3, 1, 4, 1, 5}, 'pi')
print(unique_keys)
# Output: {1: 'pi', 3: 'pi', 4: 'pi', 5: 'pi'}

The Mutable Default Value Trap

The most critical gotcha with fromkeys() is that all keys reference the same value object. For immutable types like integers, strings, or booleans, this doesn’t matter. But with mutable types like lists or dictionaries, modifications affect all keys.

# WRONG: Shared mutable reference
tasks = dict.fromkeys(['alice', 'bob', 'charlie'], [])
tasks['alice'].append('code review')
print(tasks)
# Output: {'alice': ['code review'], 'bob': ['code review'], 'charlie': ['code review']}
# All users share the same list!

# CORRECT: Dictionary comprehension with unique lists
tasks = {user: [] for user in ['alice', 'bob', 'charlie']}
tasks['alice'].append('code review')
print(tasks)
# Output: {'alice': ['code review'], 'bob': [], 'charlie': []}

This behavior occurs because Python creates one value object and assigns the same reference to all keys. To verify this:

shared = dict.fromkeys(['a', 'b', 'c'], [])
print(id(shared['a']) == id(shared['b']))  # True - same object

unique = {k: [] for k in ['a', 'b', 'c']}
print(id(unique['a']) == id(unique['b']))  # False - different objects

Practical Use Cases

Initializing Lookup Tables

Create fast membership testing structures or mapping tables:

# Valid file extensions
valid_extensions = dict.fromkeys(['.jpg', '.png', '.gif', '.webp'], True)

def is_valid_image(filename):
    ext = filename[filename.rfind('.'):].lower()
    return valid_extensions.get(ext, False)

print(is_valid_image('photo.jpg'))   # True
print(is_valid_image('doc.pdf'))     # False

Setting Default Configuration

Establish baseline configuration with predetermined keys:

# Database connection defaults
db_config = dict.fromkeys([
    'host', 'port', 'database', 'user', 'password'
], '')

# Override specific values
db_config.update({
    'host': 'localhost',
    'port': 5432,
    'database': 'production'
})

print(db_config)
# Output: {'host': 'localhost', 'port': 5432, 'database': 'production', 
#          'user': '', 'password': ''}

Creating Counter Structures

Initialize counters for known categories:

# Vote counting system
candidates = ['Alice', 'Bob', 'Charlie']
votes = dict.fromkeys(candidates, 0)

# Process votes
incoming_votes = ['Alice', 'Bob', 'Alice', 'Charlie', 'Alice']
for vote in incoming_votes:
    if vote in votes:
        votes[vote] += 1

print(votes)
# Output: {'Alice': 3, 'Bob': 1, 'Charlie': 1}

Combining with Other Dictionary Operations

The fromkeys() method works well with other dictionary operations for complex initializations:

# Create a permissions matrix
users = ['admin', 'editor', 'viewer']
actions = ['read', 'write', 'delete']

# Initialize all permissions to False
permissions = {
    user: dict.fromkeys(actions, False) 
    for user in users
}

# Set specific permissions
permissions['admin'].update(dict.fromkeys(actions, True))
permissions['editor'].update({'read': True, 'write': True})
permissions['viewer']['read'] = True

print(permissions)
# Output: {
#   'admin': {'read': True, 'write': True, 'delete': True},
#   'editor': {'read': True, 'write': True, 'delete': False},
#   'viewer': {'read': True, 'write': False, 'delete': False}
# }

Performance Considerations

The fromkeys() method is optimized for creating dictionaries with many keys sharing the same value. It’s faster than alternatives when dealing with immutable defaults:

import timeit

keys = range(10000)

# fromkeys() approach
def using_fromkeys():
    return dict.fromkeys(keys, 0)

# Dictionary comprehension
def using_comprehension():
    return {k: 0 for k in keys}

# fromkeys is typically faster for immutable defaults
print(timeit.timeit(using_fromkeys, number=10000))      # ~0.15 seconds
print(timeit.timeit(using_comprehension, number=10000)) # ~0.25 seconds

However, when you need unique mutable values, dictionary comprehensions are necessary despite being slightly slower.

Converting Existing Collections

Use fromkeys() to transform lists into dictionaries for deduplication or membership testing:

# Remove duplicates while preserving order (Python 3.7+)
items = [1, 2, 3, 2, 4, 1, 5]
unique_items = list(dict.fromkeys(items))
print(unique_items)
# Output: [1, 2, 3, 4, 5]

# Create a word frequency initializer
text = "the quick brown fox jumps over the lazy dog"
words = text.split()
word_freq = dict.fromkeys(words, 0)

# Count occurrences
for word in words:
    word_freq[word] += 1

print(word_freq)
# Output: {'the': 2, 'quick': 1, 'brown': 1, 'fox': 1, ...}

When Not to Use fromkeys()

Avoid fromkeys() when:

  1. You need unique mutable values for each key—use dictionary comprehensions instead
  2. Keys require different default values—use explicit dictionary construction
  3. Values depend on the keys—use dictionary comprehensions with conditional logic
# BAD: Trying to create unique lists
data = dict.fromkeys(['a', 'b'], [])  # Shared reference

# GOOD: Dictionary comprehension
data = {k: [] for k in ['a', 'b']}  # Unique lists

# GOOD: Key-dependent values
squares = {x: x**2 for x in range(5)}

The fromkeys() method excels at quickly initializing dictionaries with predetermined structure and immutable default values. Understanding its reference behavior prevents subtle bugs while leveraging its performance benefits for appropriate use cases.

Liked this? There's more.

Every week: one practical technique, explained simply, with code you can use immediately.