本文摘自朱雷老师所著《Python工匠》一书内容,作为笔记予以记录。
列表、元组、字典、集合是Python中4中内置容器类型,是Python语言中最为重要的组成部分,在《Python工匠》第三章容器类型中做了重要知识点的介绍,并引申出了对象的可变性、可哈希性等诸多基础概念,在其它的Python书籍中很少读到见底如此之深,特别有用的知识内容,特此记录。
内置容器功能丰富,基于它构建的自定义容器更为强大,能够帮助我们完成许多有趣的事情,在本文后面将摘录作者自定义字典类型,优化了一个日志分析脚本。
虽然无需了解类表的底层实现原理就可以使用列表,但如果能深入了解列表是基于数组实现的,就能避开一些性能陷阱,知道在什么情况下应该选择其它数据机构实现某些需求。
一、《Python工匠》第三章容器类型中的知识总结:
(1)基础知识
(2)列表与元组
(3)字典与集合
(4)代码可读性技巧
(5)代码可维护性技巧
(6)代码性能要点
二、重要知识点与使用技巧
(1)基本概念
列表(list)是一种非常经典的容器类型,通常用来存放多个同类对象,比如从1到10的所有整数。一般使用中括号[]或者list()内置函数创建,还有列表推导式。
元组(tuple)和列表非常相似,但跟列表不同,它不能够被修改。这意味着元组完成初始化后就没法再改动了(例外的是,元组中有列表元素,这个列表还是可以更改其中元素的)。一般使用圆括号()或者tuple()内置函数创建,无法直接使用推导式生成元组。
字典(dict)类型存放的是一个个键值对(key:value)。一般使用大括号{}或者dict()内置函数创建,还有字典推导式创建。
集合(set)它的特点是成员不能重复,所以经常用来去重(剔除重复元素),使用大括号表示,但是不能直接使用{}来定义一个空集合,因为{}表示一个空字典。需要使用set()函数来创建。
>>> numbers =[1,2,3,2,3,4,5]
>>> set(numbers) # 实现去重
{1, 2, 3, 4, 5}
>>>
(2)理解列表的可变性
Python里的内置数据类型,大致上可以分为可变与不可变两种。
可变(mutable):列表、字典、集合
不可变(immutable):整数、浮点数、字符串、字节串、元组
在学习Python时,理解类型的可变性是非常重要的一课。下面通过2段代码来理解最常见的场景“函数调用”来演示。
示例一:为字符串追加内容
在这个示例里,定义了一个往字符串追加内容的函数add_str(),并在外层用一个字符串参数调用该函数:
def add_str(in_func_obj):
'''给一个字符串追加内容'''
print(f'add_str函数内打印信息,追加内容之前: 函数传入变量in_func_obj = "{in_func_obj}"')
in_func_obj += ' suffix'
print(f'add_str函数内打印信息,追加内容之后: 函数传入变量in_func_obj = "{in_func_obj}"')
# 原始字符串变量
orig_obj = 'foo'
# 打印出字符串变量orig_obj的初始化值
print(f'执行add_str()函数前,打印信息,字符串变量初始化值 orig_obj = "{orig_obj}"')
# 调用add_str函数,打印的信息
add_str(orig_obj)
# 打印出字符串变量orig_obj的值,执行add_str()函数后
print(f'执行add_str()函数后,打印信息,字符串变量初始化值 orig_obj = "{orig_obj}"')
运行上面代码会发现,创建的orig_obj(字符串)对象,作为参数传入函数add_str()后,值没有变化。
示例二:为列表追加内容
在下面的代码中,保留一模一样的代码逻辑,但是把orig_obj换成了列表对象:
def add_list(in_func_obj):
'''给一个字符串追加内容'''
print(f'add_str函数内打印信息,追加内容之前: 函数传入变量in_func_obj = "{in_func_obj}"')
in_func_obj += ['baz']
print(f'add_str函数内打印信息,追加内容之后: 函数传入变量in_func_obj = "{in_func_obj}"')
# 原始字符串变量
orig_obj = ['foo','bar']
# 打印出字符串变量orig_obj的初始化值
print(f'执行add_list()函数前,打印信息,字符串变量初始化值 orig_obj = "{orig_obj}"')
# 调用add_str函数,打印的信息
add_list(orig_obj)
# 打印出字符串变量orig_obj的值,执行add_list()函数后
print(f'执行add_list()函数后,打印信息,字符串变量初始化值 orig_obj = "{orig_obj}"')
执行后会发现结果大不一样,列表变量orig_obj传入函数后,值发生了更改。
这是因为Python在进行函数调用传参时,采用的既不是值传参,也不是引用传参,而是传递了“变量所指对象的引用”。
换个角度说,相当于做了一次变量赋值,执行:in_func_obj = orig_obj
所以,在函数内部执行in_func_obj += ...等操作时,是否会影响外部变量,只取决与in_func_obj所指向的对象本身是否可变。
字符串对象在Python中是不可变的数据类型,经过赋值,是生成了一个新对象(值)in_func_obj,包括对其进行操作。
而列表是可变类型数据对象,经过赋值新变量,但是新变量依然是指向原对象(相当于起了一个别名),对其进行+=操作,原对象orig_obj值也自然是变化的。
(3)函数返回多个结果,其实就是返回元组
在Python中,函数可以一次返回多个结果,其实是通过返回一个元组来实现的。
def get_rectangle():
"""返回长方形的宽和高"""
width = 100
height = 60
return width,height
# 获取函数的多个值
regult = get_rectangle()
print(regult) # 输出(100,20),是一个元组类型
# 将函数返回值一次赋值给多个变量,其实就是对元组做了一次解包
width,height = get_rectangle()
print(width,height) # 输出:100 60