Python入门之字典

       

        映射是一种可以通过名称来访问其各个值的数据结构。字典是python中唯一的内置映射类型,其中的值不按顺序排列,而是存储在下。键可以是数字、字符串或元组。

一、字典的用途

        字典是一种键-值对(key-value pair)的数据结构,类似于现实生活中的字典。在现实生活中,字典通过单词(键)来查找对应的定义(值)。同样,在 Python 中,字典通过(key)来快速查找对应的(value) 。

  • 键:唯一标识一个值的标识符,通常是字符串、数字或其他不可变类型。
  • 值:与键关联的数据,可以是任何类型的对象(字符串、数字、列表、字典等)。      

        很多情况下使用字典比列表更合适,比如:

  • 表示棋盘的状态 :每个位置可以用坐标作为键,状态作为值。
  • 存储文件修改时间 :文件名作为键,修改时间作为值。
  • 数字电话/地址簿 :姓名作为键,电话号码作为值。
     

示例:创建电话簿

        假设有一组名字和对应的电话号码,如何高效的存储和查找这些信息?

使用列表存储:

分别创建两个列表:一个存储名字,另一个存储电话号码
names = ['Alice', 'Beth', 'Cecil', 'Dee-Dee', 'Earl']
numbers = ['2341', '9102', '3158', '0142', '5551']

#要查找某个名字对应的电话号码,可以通过名字在 names 列表中找到索引,
#然后用这个索引来访问 numbers 列表中的电话号码:
# 查找 Cecil 的电话号码
index = names.index('Cecil')  # 找到 'Cecil' 在 names 列表中的索引
phone_number = numbers[index]  # 使用索引获取对应的电话号码
print(phone_number)  # 输出: '3158'

这种方法可行,但不够直观和高效:

  • 需要维护两个列表,容易出错。
  • 每次查找都需要先找到索引,再访问另一个列表。

使用字典存储:

        字典允许我们将名字直接作为电话号码作为。这样,我们可以通过名字直接访问对应的电话号码,而不需要额外的查找步骤。

#创建字典 phonebook
phonebook={
    'Alice':'2341',
    'Beth':'9102',
    'Cecil':'3158',
    'Dee-Dee':'0142',
    'Earl':'5551'
}

#查找Cecil的电话号码
phone_number = phonebook['Cecil']
print(phone_number)#输出3158

二、创建和使用字典

        字典由键(key)和值(value)组成,这种键-值对称为(item)。上述代码已经给出了一个简单的字典,在这个示例中,键为名字:'Alice', 'Beth', 'Cecil',值为电话号码:'2341', '9102', '3258'。每个键与其值之间都用冒号(:)分隔。项之间用逗号分隔,整个字典放在花括号内。

        空字典没有任何项,用{}表示。

        在字典以及其他映射类型中,键是独一无二的,值可以重复。

2.1 函数dict

        可以使用函数dict从其他映射(如字典)或键-值对序列创建字典:

>>> items=[('name','Gumby'),('age',42)]
>>> d=dict(items)
>>> d
{'name': 'Gumby', 'age': 42}
>>> d['name']
'Gumby'

还可以使用关键字实参来调用这个函数:

>>> d=dict(name='Gumdy',age=42)
>>> d
{'name': 'Gumdy', 'age': 42}

不提供任何参数时返回空字典:

>>> empty_dict=dict()
>>> empty_dict
{}

还有一种用copy方法创建字典,在本节后面介绍。

2.2 基本的字典操作

        字典的行为在很多方面类似于序列(如列表),但也有其独特之处。以下是字典的一些基本操作:

2.2.1 len

     len(d)返回字典d包括的(键-值)对数。

phonebook = {'Alice': '2341', 'Beth': '9102', 'Cecil': '3258'}
print(len(phonebook))  # 输出: 3

2.2.2 d[k]

        d[k]返回与键k相关联的值。如果输入的键不存在,则抛出KeyError错误。

phonebook = {'Alice': '2341', 'Beth': '9102', 'Cecil': '3258'}
print(phonebook['Alice'])  # 输出: '2341'

也可以通过d[k]=v的赋值方式修改字典/添加新的键-值对:

phonebook = {'Alice': '2341', 'Beth': '9102', 'Cecil': '3258'}

# 修改已有的键值对
phonebook['Alice'] = '1234'
print(phonebook)  # 输出: {'Alice': '1234', 'Beth': '9102', 'Cecil': '3258'}

# 添加新的键值对
phonebook['Dee-Dee'] = '0142'
print(phonebook)  # 输出: {'Alice': '1234', 'Beth': '9102', 'Cecil': '3258', 'Dee-Dee': '0142'}

2.2.3  del d[k]

        通过del [k]删除键为k的项。如果输入的键不存在,则抛出KeyError错误。

phonebook = {'Alice': '2341', 'Beth': '9102', 'Cecil': '3258'}
del phonebook['Alice']
print(phonebook)  # 输出: {'Beth': '9102', 'Cecil': '3258'}

2.2.4 k in d

        可以使用k运算符检查字典中是否存在某个。若存在则返回True,否则返回False。

phonebook = {'Alice': '2341', 'Beth': '9102', 'Cecil': '3258'}
print('Alice' in phonebook)  # 输出: True
print('Dee-Dee' in phonebook)  # 输出: False

虽然字典和列表在某些方面有相似之处,但也有一些重要的不同点:

  • 键的类型:字典中的键可以是整数,但并非必须是整数。字典中的键可以是任何不可变
    的类型,如浮点数(实数)、字符串或元组。

  •  自动添加:即便是字典中原本没有的键,也可以给它赋值,这将在字典中创建一个新项。
    然而,如果不使用append或其他类似的方法,就不能给列表中没有的元素赋值。

  • 成员资格:表达式k in d(其中d是一个字典)查找的是键而不是值,而表达式v in l(其
    中l是一个列表)查找的是值而不是索引。

  • 相比于检查列表是否包含指定的值,检查字典是否包含指定的键的效率更高。数据结构
    越大,效率差距就越大。

练习1  一个简单的数据库

        创建一个将人名用作键的字典,每个人都用一个字典表示。字典包含键‘phone'和’addr',它们分别与电话号码和地址相关联。

people={
    'Alice':{
        'phone':'2341',
        'addr':'Foo drive 23'
    },
    'Beth':{
        'phone':'9102',
        'addr':'Bar street 42'
    },
    'Cecel':{
        'phone':'3158',
        'addr':'Bas avenue 90'
    }
}

#电话号码和地址的描述性标签,打印时输出使用
labels={
    'phone':'phone number',
    'addr':'address'
}

name = input('Name: ')

#要查找电话号码还是地址
request = input('Phone number (p) or address (a)?')

#使用正确的键
if request == 'p':
    key = 'phone'

elif request == 'a':
    key = 'addr'

#仅当名字是字典包含的键时才打印信息
if name in people:
    print("{}'s {} is {}".format(name,labels[key],people[name][key]))

这个程序的运行结果示例:

Name: Alice
Phone number (p) or address (a)?p
Alice's phone number is 2341

2.2.5 字符串格式化功能用于字典

        在有些情况下,通过在字典中存储一系列命名的值,可让格式设置更容易些。例如,可在字典中包含各种信息,这样只需在格式字符串中提取所需的信息即可。为此,必须使用format_map方法来指出:

>>> phonebook={'Beth':'9102','Alice':'2341','Cecil':'3258'}
>>> phonebook
{'Beth': '9102', 'Alice': '2341', 'Cecil': '3258'}
>>> "Cecil's phone number is {Cecil}.".format_map(phonebook)
"Cecil's phone number is 3258."

示例:使用字典生成html模板

#在模板系统中,常常需要根据动态数据生成 HTML 文档。
#我们可以使用 format_map() 方法,通过字典来填充模板中的占位符。
template = '''


    {title}


    

{title}

{text}

''' data = { 'title': 'My Home Page', 'text': 'Welcome to my home page!' } print(template.format_map(data))

输出:



    My Home Page


    

My Home Page

Welcome to my home page!

三、字典方法

3.1 clear

        clear方法用于删除字典中的所有键值对,使字典变为空字典。调用后,字典会变成一个空字典(即 {}),但不会返回任何值:

>>> d={}
>>> d['name']='Gumby'
>>> d['age']=42
>>> d
{'name': 'Gumby', 'age': 42}
>>> d.clear()
>>> d
{}

        clear方法的一个重要应用场景是处理共享字典的场景。如果多个变量指向同一个字典,使用clear()可以确保所有引用该字典的变量都看到字典被清空的效果:

x={}
y=x#y和x指向同一个字典

#在x中添加键值对
x['key']='value'
print(y)

#讲x重新赋值为空字典
x={}
print(x)#输出:{}
print(y)#输出:{‘key':'value'}

上述代码在清空x时,对y没有任何影响。如果要使所有指向该字典的变量都会看到字典被清空的效果,必须使用clear:

x={}
y=x#y和x指向同一个字典

#在x中添加键值对
x['key']='value'
print(y)

#讲x重新赋值为空字典
x.clear()
print(x)#输出:{}
print(y)#输出:{}

3.2 copy和deepcopy

        方法copy是字典的一个内置方法,用于浅复制字典。浅复制意味着新字典会包含原字典的所有键值对,如果原字典中的值是可变对象(列表、字典等),新字典中对应的值任然是指向同一个对象的引用。示例:

x={
    'username':'admin',
    'machines':['foo','bar','baz']
}
y=x.copy()#浅复制

print(x)# 输出: {'username': 'admin', 'machines': ['foo', 'bar', 'baz']}
print(y)# 输出: {'username': 'admin', 'machines': ['foo', 'bar', 'baz']}

#替换副本中的值
y['username']='mlh'
print(x) # 输出: {'username': 'admin', 'machines': ['foo', 'bar', 'baz']}
print(y)# 输出: {'username': 'mlh', 'machines': ['foo', 'bar', 'baz']}

#修改嵌套对象(列表)
y['machines'].remove('baz')
print(x)# 输出: {'username': 'admin', 'machines': ['foo', 'baz']}
print(y)# 输出: {'username': 'mlh', 'machines': ['foo', 'baz']}

        浅复制的优点是效率高,因为它只是创建了一个新的字典对象,并没有复制嵌套的对象(如列表、字典等)。然而,这也带来了潜在的问题:

  • 替换副本中的值不影响原字典 :如果直接替换副本中的某个键的值,原字典不会受到影响。
  • 修改嵌套对象会影响原字典 :如果修改嵌套对象(如列表、字典等),原字典中的对应对象也会被修改,因为它们共享同一个引用

为了避免浅复制带来的问题(即修改嵌套对象时原字典也被修改),可以使用深复制(Deep Copy) 。Python的copy模块提供了deepcopy()函数,可以实现深复制。深复制会递归地复制所有嵌套的对象,确保副本和原字典完全独立

from copy import deepcopy
x={
    'username':'admin',
    'machines':['foo','bar','baz']
}
y=deepcopy(x)#深复制

print(x)# 输出: {'username': 'admin', 'machines': ['foo', 'bar', 'baz']}
print(y)# 输出: {'username': 'admin', 'machines': ['foo', 'bar', 'baz']}

#替换副本中的值
y['username']='mlh'
print(x) # 输出: {'username': 'admin', 'machines': ['foo', 'bar', 'baz']}
print(y)# 输出: {'username': 'mlh', 'machines': ['foo', 'bar', 'baz']}

#修改嵌套对象(列表)
y['machines'].remove('baz')
print(x)# 输出: {'username': 'admin', 'machines': ['foo', 'bar', 'baz']}
print(y)# 输出: {'username': 'mlh', 'machines': ['foo', 'baz']}

3.3 fromkeys

        fromkeys方法创建一个新字典,其中包含指定的键,且每个键的值都是None。如果不想使用默认的None,可以提供特定的值。fromkeys的基本语法是:

{}.fromkeys(keys)

参数keys是一个可迭代的对象(如列表、元组等),包含要作为键的元素。示例:

keys=['name','age']
result={}.fromkeys(keys)
print(result)#输出 :{'name': None, 'age': None}

上述代码创建一个空字典后调用fromkeys,也可以通过dict调用:

print(dict.fromkeys(['name','age']))#输出:{'name': None, 'age': None}

 如果希望为所有键设置同一个自定义值,可以提供第二个参数:

{}.fromkeys(keys,value)

示例:

keys=['name','age']
result={}.fromkeys(keys,'(unknown)')
print(result)#输出{'name': '(unknown)', 'age': '(unknown)'}

3.4 get

        get是字典的一个非常重要的方法,相比直接通过d[key]访问字典,get方法提供了更友好的错误处理机制。

        get的基本语法是

d.get(key,default=None)

key是要查找的键,default可选,如果键不存在时返回None,如果键存在则返回对应值。示例:

d={}

#尝试直接访问不存在的键
print(d['name'])#报错:KeyError: 'name'

#使用get安全访问
print(d.get('name'))#输出:None

#指定默认值
print(d.get('name','N/A'))#输出 N/A

如果字典中存在指定的键,get的行为与直接访问一致:

d={'name':'Eric'}

#使用get安全访问
print(d.get('name'))#输出:Eric

#指定默认值,但是键存在,会被忽略
print(d.get('name','N/A'))#输出:Eric

练习2 使用get()的简单数据库

people = {
    'Alice': {'phone': '123-456-7890', 'addr': '123 Main St'},
    'Bob': {'phone': '987-654-3210', 'addr': '456 Elm St'}
}

#定义标签映射,用于将简写转换为完整名称
labels={
    'phone':'phone number',
    'addr':'address'
}

#获取用户输入的姓名
name=input('Name: ')

#获取用户想要查询的内容类型:电话号码(p)或地址(a)
request = input('Phone number (p) or address (a)?')

#确定正确的键
key = request
if request =='p':
    key = 'phone'
elif request == 'a':
    key = 'addr'

#使用get()提供默认值
person = people.get(name,{})#从people字典中获取指定姓名的联系人信息,如果不存在则返回空字典
label = labels.get(key,key)#从字典中获取完整键名,若不存在则返回原键名
result = person.get(key,'not available')#从person字典中获取指定键值,如不存在则返回‘not available“

#输出结果
print("{}'s {} is {}.".format(name,label,result))

3.5 items和字典视图

        items方法返回一个包含所有字典项的列表,其中每个元素都为(key,value)的形式。字典项在列表中的顺序不确定,取决于底层字典的实现:

d={'title':'python web site','url':'http:python.org','spam':0}
print(d.items())#输出:dict_items([('title', 'python web site'), ('url', 'http:python.org'), ('spam', 0)])

返回值属于一种名为字典视图的特殊类型。字典视图本身是只读的,不能直接对其进行修改,支持迭代和成员资格检查(len()、in等):

d={'title':'python web site','url':'http:python.org','spam':0}
print(d.items())#输出:dict_items([('title', 'python web site'), ('url', 'http:python.org'), ('spam', 0)])

it=d.items()
print(len(it))#输出3
print(('spam',0) in it)#输出True

视图的另一个优点是不复制,它们始终是底层字典的反映,当修改底层字典时,视图会自动更新:

it = d.items()
print(len(it))  # 输出: 3
print(('spam', 0) in it)  # 输出: True

# 修改底层字典
d['spam'] = 1
print(('spam', 0) in it)  # 输出: False
print(('spam', 1) in it)   # 输出: True

 如果要将字典视图转换为列表,可以使用list()函数:

print(list(d.items())) # 输出: [('title', 'Python Web Site'), ('url', 'http://www.python.org'), ('spam', 0)]

3.6 keys

        keys方法返回一个包含字典中的所有的字典视图:

print(d.keys())  # 输出: dict_keys(['title', 'url', 'spam'])

与items相同,keys视图也是底层字典的实时反映,也支持迭代和成员检查。

3.7pop

        pop方法可用于获取指定键的关联值,并将该键值对从字典删除。如果指定的键不存在,可以提供一个默认值;如果键不存在且提供默认值,则抛出KeyError错误:

d={'x':1,'y':2}
print(d.pop('x'))#输出 1
print(d)#输出 {'y': 2}

#处理键不存在的情况
print(d.pop('z', 'default_value'))  # 输出: 'default_value'
print(d) # 输出: {'x': 1, 'y': 2}

3.8popitem

        如果当前字典不为空,popitem方法随机弹出一个键值对 (即删除并返回一个键值对);如果字典为空,抛出KeyError错误:

d = {'url': 'http://www.python.org', 'spam': 0, 'title': 'Python Web Site'}
print(d.popitem())  # 输出: ('url', 'http://www.python.org')
print(d)            # 输出: {'spam': 0, 'title': 'Python Web Site'}

由于字典是无序的,所以popitem方法弹出的键值对也是随机的,即字典没有像列表一样”最后一个元素“的概念 。如果希望以可预测的顺序弹出键值对,可以使用collections模块中的OrderedDict类,它保留插入顺序。

3.9setdefault

        setdefault是字典的一个方法,用于获取指定键对应的值 ,如果键不存在,则在字典中添加该键并设置默认值None:

d={}
print(d.setdefault('name','N/A'))#输出N/A
print(d)#输出{'name': 'N/A'}

#修改默认值
d['name']='Gumdy'
print(d.setdefault('name','New Default'))#输出 Gumdy
print(d)#输出{'name': 'Gumdy'}

        get和setdefault方法都可以获取值,但是get不会修改字典,而setdefault会在键不存在时添加默认值。

3.10update

        update方法用于更新字典的内容 。它可以将另一个字典键值对序列中的内容合并到当前字典中,如果当前字典中已经存在相同的键,则替换其值。

d={'title':'python web site','url':'http:python.org','changed':'mar14 22:09:15 MET 2016'}
x={'title':'python language website'}
d.update(x)
print(d)  # 输出: {'title': 'python language website', 'url': 'http:python.org', 'changed': 'mar14 22:09:15 MET 2016'}

update方法支持多种形式的输入:

#字典
d.update({'new_key':'new_value'})

#键值对序列
d.update([('new_key','new_value')])

#关键字序列
d.update(new_key='new_value')

3.11values

        values方法用于返回一个包含所有的字典视图,顺序不确定。

d={1:1,2:2,3:3,4:1}
print(d.values())# 输出: dict_values([1, 2, 3, 1])

不同于方法keys,方法values返回的视图可能包含重复的值,因为字典允许不同的键对应相同的值。

四、迭代字典

        要遍历字典的所有关键字,可像遍历序列那样使用普通的for语句。

d = {'x': 1, 'y': 2, 'z': 3}
for key in d:
    print(key)

输出:

x
y
z

 如果需要访问对应的值,可以通过 d[key] 获取。

d = {'x': 1, 'y': 2, 'z': 3}
for key in d:
    print(f"{key} corresponds {d[key]}")

如果只想遍历字典的值,可以使用values()方法

d = {'x': 1, 'y': 2, 'z': 3}
for value in d.values():
    print(value)

        如果需要同时获取键和值,可以使用 items() 方法。items() 返回一个包含键值对的视图对象,每个键值对是一个元组 (key, value)

d = {'x': 1, 'y': 2, 'z': 3}
for key, value in d.items():
    print(f"{key} corresponds to {value}")

你可能感兴趣的:(python,开发语言)