引言——在Python中,通过数据结构来保存项目中重要的数据信息。Python语言内置了多种数据结构,例如列表,元组,字典和集合等。本堂课我们来讲一讲Python中举足轻重的一大数据结构——列表(列表也被称为序列),它是Python语言中最基本的一种数据结构,我们可以将它比拟其他编程语言(C/C++/Java)中的数组。
基本概念:在Python程序中使用中括号"[]"来表示列表,并用逗号分隔其中的元素。
books = ['python从入门到放弃', 'C从入门到放弃', '汇编从入门到放弃'] # 创建一个名为books的列表
print(books) # 输出列表books中的信息
代码讲解:在上述代码中,创建一个名为“books”的列表,在列表中存储了三个元素,执行后会将列表打印输出。
注意:索引及切片(分片)对于任何数据类型都有效哦!
在python程序中,因为列表是一个有序集合,所以要想访问列表中的任何元素,只需要将该元素的位置或索引告诉Python即可(但是要注意的是:列表中元素的索引是从0开始!)。要想访问列表元素,可以指出列表的名称,再指出列表的索引,并将其放在方括号内。
骚操作来袭:如果在一个项目中,我们不知道存储项目数据的这个列表到底多长,而我们就是要它最后一个元素该怎么做呢?
其实方法很多很多哦!但是老师在这里讲一个通过下标取它的骚操作:
a = [1,2,3,4,5,6,7,8,9,10]
print(a[-1]) # 输出为 10。 从列表右侧向左侧第一个是-1,以此类推。
切片
(万能公式一大注意点:左闭右开!)
第一个:
正方向[起始位置:结束位置+1:步长]
第二个:
注(如果步长为负数,则逆序)
反方向[开始位置:结束位置-1:负数]
是不是看了万能公式后一头懵,那就对了,都给我打起精神,咱们一起来好好研究这个万能公式究竟是个啥意思!
知识补给站(一个简便的创建数字列表的方法——使用方法range(),注意这个方法也是左闭右开哦!):
上代码:
numbers = list(range(0,11))
print(numbers) # 输出[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
正方向的意思是从列表左到右取值!
起始位置是想要获取的列表元素的起始索引;
结束位置是想要获取的列表元素的结束索引加一。
步长是每一次取值跳几个索引,默认值是1。
numbers = list(range(0,11))
print(numbers[0:5]) # 等价于:print(numbers[0:5:1])
给定列表a = [1,2,3,4,5,6,7,8,9,10],我们需求是取出列表中所有奇数——这就用到了正向切片!
a = [1,2,3,4,5,6,7,8,9,10]
print(a[0:(9+1):2]) # print(a[0(起始位置):9+1(结束位置+1):2(隔两个取一次)])
课上小问题(会的举手快快快——加平时成绩哦!)
如何取该列表中的所有偶数呢?
答案:
print(a[1(起始位置):9+1(结束位置+1):2(隔两个取一次)])
运行结果为 [2,4,6,8,10]
注:如果步长为负数,则逆序!
正方向的意思是从列表左到右取值!
起始位置是想要获取的列表元素的结束索引(包括这个元素哦!);
结束位置是想要获取的列表元素的起始索引减一。
步长是每一次取值跳几个索引,如果是逆序切片就必须给负数的步长!。
a = [1,2,3,4,5,6,7,8,9,10]
print(a[9:1:-1])
给定列表a = [1,2,3,4,5,6,7,8,9,10],我们需求是倒序取出列表中所有偶数——这就用到了逆序切片!
a = [1,2,3,4,5,6,7,8,9,10]
print(a[::-2]) # 简写了,起始位置和结束位置两个地方没写,则为整个列表
① X.append() 加入单个值!!! 列表专属
a = [1,2,3]
a.append('s')
print(a) # 输出为:[1, 2, 3, 's']
** ② insert(x,y) —>自定义位置添加元素,将元素插入到指定的索引 加入单个值!!!(参数含义:第一个是所添加的下标指定位置,第二个是需要添加的元素)**
a = [1,2,3]
a.insert(1,'new_num') #
print(a) # 输出为:[1, 'new_num', 2, 3]
③ extend() —>在列表末尾插入,可插入序列类型(字符串,列表,元组),会拆分每一个元素。 加入多个值!!!
a = [1,2,3]
a.extend('abc')
print(a) # 输出为:[1, 2, 3, 'a', 'b', 'c']
**④pop() —>没有传参数的情况下会删除列表的最后一个元素,如果带了参数的话就删除指定索引位置的元素 **
# 未加参数——删除最后一个元素!
a = [1,2,3]
a.pop()
print(a) # 输出为[1,2]
加参数——删除指定索引位置的元素!
b = [1,2,3]
a.pop[1]
print(a) # 输出为[1,3]
⑤remove() —>删除指定元素,但是在有多个相同项的情况下只删除第一个
a = [1,2,3,4,1,1,1]
a.remove(1)
print(a) # 输出为[2,3,4,1,1,1]
**⑥clear() —>清空列表,删除列表里所有的值,括号内不需要带参数
**
a = [1,2,3,4,1,1,1]
a.clear()
print(a) # 输出为[]
⑦count() —>统计重复参数重复了几次,计数(可以赋值可以print)
a = [1,2,3,4,1,1,1]
num = a.count(1)
print(num) # 输出为 4
⑧index(x,y) —>第一个参数是要查找的值,第二个参数是查找开始的下标位置。每次查找只会显示查找到的第一个值的下标。不加第二个参数的话默认从下标0开始,没有查找到的时候会报错 (可以赋值可以print)
a = [1,2,3,4,1,1,1]
index1 = a.index(1)
index2 = a.index(1,2)
print(index1) # 输出为 0
print(index2) # 输出为 4
⑨ join() —>方法用于将序列中的元素以指定的字符连接生成一个新的字符串。使用方法:str.join(sequence),sequence为要连接的元素序列。返回通过指定字符连接序列中元素后生成的新字符串。
a = ['a', 'b', 'c']
end = "-".join(a)
print(end) # 输出为a-b-c
⑩列表操作中还有深浅复制的方法,具体看这篇文详解——一节课让你彻底搞懂python里面试最常问问题之一深浅复制——看完不懂你来揍我!(三好学生都已经拿出笔记本认真上课记笔记了~)
拓展:列表排序的方法!
①sort() --->对列表里的数值从小到大排序,只能是数字,如果列表里有字符串的话那么需要在括号内加入:key=str 如a.sort(key=str) 。字符串的排序根据于ASCII的字母排序。
上代码:
a = ['a', 'd', 'f', 'b']
a.sort(key=str)
print(a) # 输出为:['a', 'b', 'd', 'f']
拓展:使用sort()方法对列表里的数值从大到小排序;如果列表里有字符串的话加入参数key=str,如a.sort(key=str,reverse=True)。
上代码:(列表含有字符串)
a = ['a', 'g', 'b', 'c']
a.sort(key=str,reverse=True)
print(a) # 输出为:['g', 'c', 'b', 'a']
上代码:(列表元素都是数字)
a = [1,9,3,7]
a.sort(reverse=True)
print(a) # 输出为:[9,7,3,1]
②reverse() --->对列表里的数值从大到小反序,只能是数字。
上代码:
a = [1, 4, 3, 8]
a.reverse()
print(a) # 输出为: [8,3,4,1]
如果有一天,同学们在工作,Boss突然抛出来一个需求:**在python程序中如何找到列表中出现次数最多的元素!**难道你要使用循环啥的一步步瞎搞?效率低B格也不高。这里为师教你们一种简单易上手B格高的方法——使用collections模块中的Counter类,调用Counter类中的most_common()函数来实现!
直接上代码:
from collections import Counter
words = [
'look', 'into', 'my', 'AAA', 'look', 'into', 'my', 'AAA',
'the', 'AAA', 'the', 'eyes', 'not', 'BBB', 'the', 'AAA',
"don't", 'BBB', 'around', 'the', 'AAA', 'look', 'into',
'BBB', 'AAA', 'BBB', 'under'
]
word_counts = Counter(words)
print('统计所有元素出现次数:',word_counts)
top_three = word_counts.most_common(3)
print('统计出现次数最多的三个元素:',top_three)
输出为:
统计所有元素出现次数: Counter({
'AAA': 6, 'the': 4, 'BBB': 4, 'look': 3, 'into': 3, 'my': 2, 'eyes': 1, 'not': 1, "don't": 1, 'around': 1, 'under': 1})
统计出现次数最多的三个元素: [('AAA', 6), ('the', 4), ('BBB', 4)]
项目背景:如果我们在一个项目中,一个类定义的实例有很多个,而我们的需求是将这些实例排序该怎么做呢?
使用内置函数sorted()可以接收一个用来传递可调用(callable)对象的参数key,而这个可调用对象会返回待排序对象中的某些值,sorted()函数则利用这些值来比较对象。
假如在程序中存在多个User对象的实例,如果想通过属性user_id来对这些实例进行排序,可以提供一个可调用对象,它将User实例作为输入,然后返回user_id。下面代码演示排序上述User对象实例的过程:
class User:
def __init__(self, user_id):
self.user_id = user_id
def __repr__(self):
return 'User({})'.format(self.user_id)
# 原来的排序
users = [User(91), User(17), User(18)]
print(users)
# 根据user_id排序——两种方法
# ①使用lambda表达式:
print(sorted(users, key=lambda u: u.user_id))
# ②使用内置函数operator.attrgetter()进行处理:
from operator import attrgetter
print(sorted(users, key=attrgetter('user_id')))
项目问题背景:
在python程序中,有时会发现编写的代码由于过度的使用硬编码的切片索引(就像上面第二小节课讲的那样!),而使得我们的项目代码变得杂乱无章而无法阅读,此时就需要清理它们。同时过度的使用硬编码的索引值,也会降低代码的可读性和可维护性。
解决方法:
在python程序中,使用函数slice()可以实现切片对象,能够在切片操作函数中实现参数传递功能,可以被用在任何允许进行切片操作的地方。
使用函数slice()的语法格式:
class slice(stop)
class slice(start, stop, step)
start:起始位置;
stop:结束位置;
step:间距。
上代码讲解:
items = [0, 1, 2, 3, 4, 5, 6]
a = slice(2,4) # 定义一个slice对象实例a
print(items[2:4]) # 使用常用的切片取列表值
print(items[a]) # 使用slice对象实例a取列表值
items[a] = [10, 11]
print(items)
print(a.start) # 分别通过属性a.start,a.stop,a.step获取该slice对象的信息
print(a.stop)
print(a.step)
s = 'verygoodman'
# 使用indices(size)函数将切片映射到特定大小的序列上,这将会返回一个(start,stop,step)元组,
# 所有的值都已经正好限制在边界以内,这样当进行索引操作时可以避免出现IndexError异常。
print(a.indices(len(s)))
print(*a.indices(len(s))) # 分解元组
for i in range(*a.indices(len(s))):
print(s[i])
深入讲解——使用indices(size)函数的好处,以及上述注释中写的为何使用此函数当进行索引操作时可以避免出现IndexError异常!
直接上代码讲解:
a = slice(2,4) # 定义一个slice对象实例a
s = 'ver' # 此时序列s
# 使用indices(size)函数将切片映射到特定大小的序列上,这将会返回一个(start,stop,step)元组,
# 所有的值都已经正好限制在边界以内,这样当进行索引操作时可以避免出现IndexError异常。
print(a.indices(len(s)))
print(*a.indices(len(s))) # 分解元组
for i in range(*a.indices(len(s))):
print(s[i])
一节课让你彻底搞懂python中的单星号(*)和双星号(**)的区别及项目实际用法——给我学!
观察运行结果会发现:我们虽然定义的slice对象实例的切片范围是2-4,但是由于映射到的序列整体范围只有0-3(左闭右开),因为我们使用了indices(size)函数,它会将切片范围限制在这个映射的序列范围边界以内:2-3,这样虽然我们定义的slice对象实例范围超出了此序列s的范围,但是因为indices(size)函数的使用并不会报错,而是类似于动态的自适应变化!
列表推导式(List Comprehension)是一种简化代码的优美方法。官方文档——**列表推导式提供了一种创建列表的简洁方法。**使用列表推导式能够非常简洁的构造一个新列表,只需要用一个简洁的表达式即可对得到的元素进行转换变形。
使用Python列表推导式的语法格式:
variable = [out_exp_res for out_exp in input_list if out_exp == 2]
out_exp_res:列表生成元素表达式,可以是有返回值的函数;
for out_exp in input_list:迭代input_list,将out_exp传入out_exp_res表达式中;
if out_exp == 2:判断根据条件可以过滤哪些值。
惯性思维——常规方法解决:
list_end = []
for x in range(10):
list_end.append(x**2)
print(list_end)
借由列表推导式——一行代码解决:
list_end1 = [x**2 for x in range(10)]
print(list_end1)
传统方法实现:
nums = []
for x in range(30):
if x % 3 == 0:
nums.append(x)
print(nums)
使用列表推导式一行代码解决:
nums1 = [x for x in range(30) if x % 3 == 0]
print(nums1)
需求:首先获取30以内能够整除3的整数,然后一次输出所获得整数的平方。
def squared(x):
return x**2
end = [squared(x) for x in range(30) if x % 3 == 0]
print(end)
在python程序中,有时候筛选列表中的某些特定元素时,筛选标准无法简单的表示在列表推导式或生成器表达式中,例如当筛选过程涉及异常处理或者其他一些复杂的细节时。此时可以考虑将处理筛选功能的代码放到单独的功能函数中,然后使用内建的filter()函数进行处理。
需求:筛选指定列表中的所有整数元素!
values = ['1', '2', '-3', '-', '4', 'N/A', '5']
def is_int(val):
try:
x = int(val)
return True
except ValueError:
return False
# 注意:因为使用函数filter()创建了一个迭代器,所以想要得到一个列表形式的结果,必须在filter()前面加上list()函数。
isvals = list(filter(is_int, values))
print(isvals)
需求:删除列表中重复出现的元素,并且保持剩下元素的显示顺序不变。如果序列中保存的元素是可散列的(hashable),那么此功能可以使用集合和生成器实现。
知识点补给站:
如果一个对象是可散列的,那么在它的生存期内必须是不可变的,这需要有一个__hash__()方法。在python程序中,
整数,浮点数,字符串和元组都是不可变的。
上代码:
def fun(items):
seen = set()
for item in items:
if item not in seen:
yield item
seen.add(item)
if __name__ == '__main__':
a = [5, 3, 3, 8, 6, 5, 9]
print(a)
print(list(fun(a)))
上述代码有个缺陷——只有当序列中的元素是可散列的时候才能实现功能。如果想在不可散列的对象序列中实现此功能,该如何实现呢?
上代码:
def fun(items, key=None):
seen = set()
for item in items:
val = item if key is None else key(item)
if val not in seen:
yield item
seen.add(val)
if __name__ == '__main__':
a = [
{
'x' :2, 'y':3},
{
'x' :1, 'y':4},
{
'x' :2, 'y':3},
{
'x' :2, 'y':3},
{
'x' :10, 'y':15}
]
print(a)
print(list(fun(a, key=lambda a: (a['x'], a['y']))))
注意:上述代码中函数fun()中的参数key的功能是将序列中的元素转换为可散列的类型,这样做的目的是检测重复选项。