当前位置:首页 > Python > 正文

Python defaultdict使用可调用对象完全指南 | 高效字典处理

Python defaultdict使用可调用对象

全面指南:高效处理字典中缺失键值的情况

defaultdict简介

defaultdict是Python标准库collections模块中的一个类,它继承自内置的dict类。与普通字典不同,当尝试访问一个不存在的键时,defaultdict会自动创建该键并为其生成一个默认值。

基本语法

from collections import defaultdict

# 创建一个defaultdict,指定默认值工厂函数
d = defaultdict(default_factory)

其中default_factory是一个可调用对象(函数、lambda表达式、类等),当访问不存在的键时,defaultdict会调用这个函数来生成默认值。

可调用对象基础

在Python中,可调用对象是指任何可以通过函数调用操作符()来调用的对象。主要包括:

  • 内置函数:如int, list, str
  • 用户自定义函数:使用def关键字定义的函数
  • Lambda表达式:匿名函数
  • :调用类会创建该类的新实例
  • 实现了__call__方法的对象

在defaultdict中,这个可调用对象应该是一个不需要参数的函数,或者所有参数都有默认值的函数。

内置函数作为工厂

Python的内置函数常被用作defaultdict的工厂函数,下面是一些常见用法:

示例1:使用int作为工厂

from collections import defaultdict

# 使用int作为默认工厂,默认值为0
count_dict = defaultdict(int)

words = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']

for word in words:
    count_dict[word] += 1

print(count_dict)
# 输出: defaultdict(<class 'int'>, {'apple': 3, 'banana': 2, 'orange': 1})

示例2:使用list作为工厂

from collections import defaultdict

# 使用list作为默认工厂,默认值为空列表
group_dict = defaultdict(list)

data = [('fruit', 'apple'), ('fruit', 'banana'), 
        ('vegetable', 'carrot'), ('fruit', 'orange'),
        ('vegetable', 'potato')]

for category, item in data:
    group_dict[category].append(item)

print(group_dict)
# 输出: defaultdict(<class 'list'>, {
#   'fruit': ['apple', 'banana', 'orange'],
#   'vegetable': ['carrot', 'potato']
# })

Lambda函数使用

当内置函数无法满足需求时,可以使用lambda表达式创建更复杂的默认值。

示例:使用Lambda表达式

from collections import defaultdict

# 使用lambda设置默认值为一个字典
nested_dict = defaultdict(lambda: defaultdict(int))

data = [('A', 'X', 10), ('A', 'Y', 5), 
        ('B', 'X', 7), ('A', 'X', 3)]

for category, subcat, value in data:
    nested_dict[category][subcat] += value

print(nested_dict['A']['X'])  # 输出: 13
print(nested_dict['B']['Y'])  # 输出: 0 (自动创建)

自定义函数示例

对于更复杂的场景,可以定义自己的函数作为工厂函数。

示例:自定义默认值函数

from collections import defaultdict
import random

def random_color():
    """返回一个随机的RGB颜色元组"""
    return (random.randint(0, 255), 
            random.randint(0, 255), 
            random.randint(0, 255))

# 使用自定义函数作为默认工厂
color_dict = defaultdict(random_color)

print(color_dict['background'])  # 例如: (123, 45, 210)
print(color_dict['text'])        # 例如: (30, 180, 75)
print(color_dict['background'])  # 与第一次访问相同

类作为工厂

类也可以作为defaultdict的工厂函数,每次访问新键时会创建该类的一个新实例。

示例:使用类作为工厂

from collections import defaultdict

class Person:
    def __init__(self, name=None):
        self.name = name
        self.attributes = {}
    
    def __repr__(self):
        return f"Person(name={self.name!r}, attributes={self.attributes})"

# 使用Person类作为工厂
people_dict = defaultdict(Person)

# 设置一些属性
people_dict['alice'].name = 'Alice'
people_dict['alice'].attributes['age'] = 30

people_dict['bob'].name = 'Bob'
people_dict['bob'].attributes['age'] = 25

print(people_dict['alice'])
# 输出: Person(name='Alice', attributes={'age': 30})

# 访问不存在的键会创建一个新的Person实例
print(people_dict['charlie'])
# 输出: Person(name=None, attributes={})

常见问题解答

Q: defaultdict与普通字典的setdefault方法有什么区别?

A: setdefault方法在键不存在时设置默认值并返回该值,而defaultdict在访问时自动创建默认值。defaultdict通常更高效,特别是需要多次设置默认值的场景。

Q: 工厂函数可以有参数吗?

A: defaultdict的工厂函数必须是不需要参数的函数。如果需要参数,可以包装在lambda中:defaultdict(lambda: your_function(arg))

Q: 如何改变已存在的defaultdict的默认工厂?

A: 可以通过设置default_factory属性:d.default_factory = new_factory。设置为None可恢复为普通字典行为(访问不存在的键会引发KeyError)。

动手练习

尝试修改下面的代码,观察defaultdict的行为:

代码编辑器

from collections import defaultdict

def default_factory():
    return "Default Value"

d = defaultdict(default_factory)
d['name'] = 'Alice'

# 尝试访问存在的键
print("Key 'name':", d['name'])

# 尝试访问不存在的键
print("Key 'age':", d['age'])

输出结果

Key 'name': Alice

Key 'age': Default Value

练习建议:

  • 尝试将工厂函数改为 int 并访问数字键
  • 尝试使用 lambda: 100 作为工厂函数
  • 尝试嵌套 defaultdict 结构

© 2023 Python编程教程 | defaultdict使用指南

本教程仅用于学习目的,转载请注明出处

发表评论