codeye
2022/09/26阅读:20主题:默认主题
Python list 常见用法
测试你对Python列表知识的60个问题 通过掌握列表基础知识来压制算法问题
图片来自Pexels的Andrew Neel 我最近做了很多算法题,发现自己对列表的理解并不尽如人意。
这是我写的60个列表问题的汇编,以评估我自己的知识。
我希望你会发现它和写它对我一样有用。
问题1-10。
-
检查一个列表是否包含一个元素 如果一个特定的元素在一个列表中,in运算符将返回True。
li = [1,2,3,'a','b','c']
'a' in li
#=> True
-
如何同时在2个以上的列表上进行迭代 你可以压缩()列表,然后迭代zip对象。zip对象是一个图元的迭代器。
下面我们同时对3个列表进行迭代,并将数值插值为一个字符串。
name = ['Snowball', 'Chewy', 'Bubbles', 'Gruff']
animal = ['Cat', 'Dog', 'Fish', 'Goat']
age = [1, 2, 2, 6]
z = zip(name, animal, age)
z #=> <zip at 0x111081e48>
for name,animal,age in z:
print("%s the %s is %s" % (name, animal, age))
#=> Snowball the Cat is 1
#=> Chewy the Dog is 2
#=> Bubbles the Fish is 2
#=> Gruff the Goat is 6
-
你什么时候会使用列表和字典?
列表和字典的使用情况一般略有不同,但也有一些重叠的地方。
我得出的算法问题的一般规则是,如果你能同时使用,就使用字典,因为查找的速度更快。
列表
如果你需要存储某些东西的顺序,就使用列表。
例如:数据库记录的id,按其显示的顺序。
ids = [23,1,7,9]
虽然从 python 3.7 开始,列表和字典都是有序的,但列表允许重复的值,而字典不允许重复的键。
字典
如果你想计算某个东西的出现次数,可以使用字典。比如家里有多少只宠物。
pets = {'dogs':2, 'cats':1, 'fish':5}
。 每个键在一个字典中只能存在一次。注意,键也可以是其他不可变的数据结构,如图元。例如:
{('a',1):1, ('b',2):1}
。
-
列表是可变的吗?
是的。请注意下面的代码,在内存中与同一标识符相关的值并没有改变。
x = [1]
print(id(x),':',x) #=> 4501046920 : [1]
x.append(5)
x.extend([6,7])
print(id(x),':',x) #=> 4501046920 : [1, 5, 6, 7]
-
列表必须是同质的吗?
不需要。不同类型的对象可以混合在一个列表中。
a = [1,'a',1.0,[]]
a #=> [1, 'a', 1.0, []]
-
append和extend之间的区别是什么?
.append()
将一个对象添加到一个列表的末尾。
a = [1,2,3]
a.append(4)
a #=> [1, 2, 3, 4]
这也意味着追加一个列表将整个列表作为一个元素添加,而不是追加它的每个值。
a.append([5,6])
a #=> [1, 2, 3, 4, [5, 6]]
.extend()
将第二个列表中的每个值作为它自己的元素加入。所以用另一个列表来扩展一个列表,将它们的值结合起来。
b = [1,2,3]
b.extend([5,6])
b #=> [1, 2, 3, 5, 6]
-
python 列表是存储值还是指针?
Python 列表本身并不存储值。它们存储指向内存中其他地方的值的指针。这允许列表是可变的。
这里我们初始化值 1 和 2,然后创建一个包括值 1 和 2 的列表。
print( id(1) ) #=> 4438537632
print( id(2) ) #=> 4438537664
a = [1,2,3]
print( id(a) ) #=> 4579953480
print( id(a[0]) ) #=> 4438537632
print( id(a[1]) ) #=> 4438537664
注意到这个列表有自己的内存地址。但是列表中的1和2与我们之前定义的1和2指向内存中的同一位置。
8. del
是做什么的? del
从一个列表中删除一个给定索引的项目。
这里我们将删除索引1的值。
a = ['w', 'x', 'y', 'z']
a #=> ['w', 'x', 'y', 'z']
del a[1]
a #=> ['w', 'y', 'z']
注意del并没有返回被删除的元素。
-
remove
和pop
之间的区别是什么?
.remove()
删除一个匹配对象的第一个实例。下面我们删除第一个b。
a = ['a', 'a', 'b', 'b', 'c', 'c']
a.remove('b')
a #=> ['a', 'a', 'b', 'c', 'c']
.pop()
通过索引删除一个对象。
pop和del的区别在于,pop返回被弹出的元素。这允许像堆栈一样使用一个列表。
a = ['a', 'a', 'b', 'b', 'c', 'c']
a.pop(4) #=> 'c'。
a #=> ['a', 'a', 'b', 'b', 'c']
默认情况下, 如果没有指定索引, pop会从一个列表中删除最后一个元素.
-
删除列表中的重复元素
如果你不关心保持列表的顺序,那么转换为集合,再转换为列表就可以实现这个目的。
li = [3, 2, 2, 1, 1, 1].
list(set(li)) #=> [1, 2, 3]
问题11-20
-
找到第一个匹配元素的索引
例如,你想在一个水果的列表中找到第一个 "苹果"。 使用.index()
方法。
fruit = ['pear', 'orange', 'apple', 'grapefruit', 'apple', 'pear']
fruit.index('apple') #=> 2
fruit.index('pear') #=> 0
-
从一个列表中删除所有元素
与其创建一个新的空列表,我们不如用.clear()
清除现有列表中的元素。
fruit = ['pear', 'orange', 'apple']
print( fruit ) #=>
['pear', 'orange', 'apple']
print( id(fruit) ) #=>
4581174216
fruit.clear()
print( fruit ) #=> []
print( id(fruit) ) #=> 4581174216
或者用del
fruit = ['pear', 'orange', 'apple']
print( fruit ) #=> ['pear', 'orange', 'apple']
print( id(fruit) ) #=> 4581166792
del fruit[:]
print( fruit ) #=> []
print( id(fruit) ) #=> 4581166792
-
遍历一个列表中的值和它们的索引
enumerate()在作为参数传递的列表中添加一个计数器。
下面我们对列表进行迭代,并将值和索引都传到字符串插值中。
grocery_list = ['flour','cheese','carrots']
for idx,val in enumerate(grocery_list):
print("%s: %s" % (idx, val))
#=> 0: flour
#=> 1: cheese
#=> 2: carrots
-
如何将两个列表连接起来
+
运算符将串联两个列表。
one = ['a', 'b', 'c']
two = [1, 2, 3]
one + two #=> ['a', 'b', 'c', 1, 2, 3]
-
如何用列表理解法操作列表中的每个元素
下面我们返回一个新的列表,每个元素都加了1。
li = [0,25,50,100]。
[i+1 for i in li]
#=> [1, 26, 51, 101]
-
计算一个特定对象在列表中的出现次数
count()
方法返回一个特定对象的出现次数。下面我们返回字符串 "fish "在一个叫做pets的列表中的出现次数。
pets = ['dog', 'cat', 'fish', 'fish', 'cat']
pets.count('fish')
#=> 2
-
如何浅层复制一个列表?
.copy()
可以用来浅层复制一个列表。
下面我们创建一个round1的浅层拷贝,将其分配给一个新的名字round2,然后删除sonny chiba这个字符串。
round1 = ['chuck norris', 'bruce lee', 'sonny chiba']
round2 = round1.copy()
round2.remove('sonny chiba')
print(round1) #=> ['Chuck Norris', 'Bruce Lee', 'sonny chiba']
print(round2) #=> ['Chuck Norris', 'Bruce Lee']
-
为什么要创建一个列表的浅层拷贝?
在前面的例子基础上,如果我们不创建一个浅层拷贝,修改round2就会修改round1。
round1 = ['chuck norris', 'bruce lee', 'sonny chiba']
round2 = round1
round2.remove('sonny chiba')
print(round1) #=> ['Chuck Norris', 'Bruce Lee']
print(round2) #=> ['Chuck Norris', 'Bruce Lee']
如果没有浅层拷贝,round1和round2只是指向内存中同一个列表的名字。这就是为什么看起来改变一个的值会改变另一个的值。
-
如何深度复制一个列表?
为此我们需要导入 copy 模块,然后调用 copy.deepcopy()
下面我们创建一个列表的深度拷贝,round1称为round2,更新round2的一个值,然后打印这两个值。在这种情况下,round1不受影响。
round1 = [
['Arnold', 'Sylvester', 'Jean Claude'],
['Buttercup', 'Bubbles', 'Blossom']
]
import copy
round2 = copy.deepcopy(round1)
round2[0][0] = 'Jet Lee'
print(round1)
#=> [['Arnold', 'Sylvester', 'Jean Claude'], ['Buttercup', 'Bubbles', 'Blossom']]
print(round2)
#=> [['Jet Lee', 'Sylvester', 'Jean Claude'], ['Buttercup', 'Bubbles', 'Blossom']]
上面我们可以看到,改变round2中的嵌套数组并没有更新round1。
-
深拷贝和浅拷贝的区别是什么? 在前面的例子基础上,创建一个浅层拷贝,然后修改它,会影响到原来的列表。
round1 = [
['Arnold', 'Sylvester', 'Jean Claude'],
['Buttercup', 'Bubbles', 'Blossom']
]
import copy
round2 = round1.copy()
round2[0][0] = 'Jet Lee'
print(round1)
#=> [['Jet Lee', 'Sylvester', 'Jean Claude'], ['Buttercup', 'Bubbles', 'Blossom']]
print(round2)
#=> [['Jet Lee', 'Sylvester', 'Jean Claude'], ['Buttercup', 'Bubbles', 'Blossom']]
为什么会发生这种情况?
创建一个浅层拷贝确实在内存中创建了一个新的对象,但是它充满了对现有对象的引用,与之前的列表相同。
创建一个深层拷贝会创建原始对象的副本并指向这些新的版本。因此,新列表完全不受旧列表变化的影响,反之亦然。
问题21-30
-
列表和元组之间有什么区别。
元组在创建后不能被更新。添加/删除/更新一个现有的元组需要创建一个新的元组。
列表在创建后可以被修改。
元组通常代表一个对象,如从数据库加载的记录,其中的元素是不同的数据类型。
Lists一般用于存储一个特定类型的对象的有序序列(但不一定)。
两者都是序列,允许重复的值。
-
返回一个列表的长度 len()
可以返回一个列表的长度。
li = ['a', 'b', 'c', 'd', 'e']
len(li)
#=> 5
但是注意它计算的是顶层对象,所以一个由几个整数组成的嵌套列表只会被算作一个对象。下面,li的长度是2,而不是5。
li = [[1,2],[3,4,5]] 。
len(li)
#=> 2
-
列表和集合的区别是什么?
列表是有序的, 而集合不是. 这就是为什么使用set来寻找列表中的唯一值, 像
list(set([3, 3, 2, 1]))
会改变顺序.
列表经常被用来追踪顺序,而集合则经常被用来追踪存在。
列表允许重复,但集合中的所有值根据定义是唯一的。
-
如何检查一个元素是否不在列表中?
为此,我们使用in运算符,但在其前面加上not。
li = [1,2,3,4] 。
5 not in li #=> True
4 not in li #=> False
-
用map函数将一个列表中的每个元素乘以5
map()
允许在一个序列上迭代,并用另一个函数来更新每个值。 map()
返回一个map对象,但我用一个列表理解来包装它,所以我们可以看到更新的值。
def multiply_5(val):
返回 val * 5
a = [10,20,30,40,50]
[val for val in map(multiply_5, a)]。
#=> [50, 100, 150, 200, 250]
-
用zip函数将两个列表合并为一个图元列表
zip()
将多个序列合并成一个图元的迭代器,在同一序列索引的值被合并在同一个图元中。
alphabet = ['a', 'b', 'c']
integers = [1, 2, 3]
list(zip(alphabet, integers))
-
在现有列表的特定索引处插入一个值 insert()
方法接收一个要插入的对象和要插入的索引。
li = ['a', 'b', 'c', 'd', 'e']
li.insert(2, 'HERE')
li #=> ['a', 'b', 'here', 'c', 'd', 'e']
注意,之前在指定索引处的元素被移到了右边,而不是被覆盖。
-
用 reduce 函数将一个列表中的值从第一个元素中减去 reduce()
需要从functools
中导入。
给定一个函数,reduce 遍历一个序列并在每个元素上调用该函数。在下一个元素上调用函数时,前一个元素的输出被作为参数传递。
from functools import reduce
def subtract(a,b):
return a - b
numbers = [100,10,5,1,2,7,5]
reduce(subtract, numbers) #=> 70
上面我们从100中减去了10、5、1、2、7和5。
-
用filter函数从一个列表中删除负值 filter()
将从一个序列中移除该函数没有返回 True 的任何元素。
下面我们删除小于 0 的元素。
def remove_negatives(x):
return True if x >= 0 else False
a = [-10, 27, 1000, -1, 0, -30]
[x for x in filter(remove_negatives, a)]
#=> [27, 1000, 0]
-
将一个列表转换成一个字典,其中列表元素是键 为此我们可以使用字典的理解力。
li = ['The', 'quick', 'brown',
'fox', 'was', 'quick']
d = {k:1 for k in li}
d #=> {'The': 1, 'quick': 1,
'brown': 1, 'fox': 1, 'was': 1}
问题31-40
-
用lambda函数修改一个现有的列表 让我们把之前写的map函数,用 lambda
把它变成一个单行代码。
a = [10,20,30,40,50]
list(map(lambda val:val*5, a))
#=> [50, 100, 150, 200, 250]
我可以把它作为一个map对象,直到我需要对它进行迭代,但我转换为一个列表,以显示里面的元素。
-
删除列表中特定索引后的元素
使用slice语法,我们可以返回一个新的列表,其中只有特定索引之前的元素。
li = [1,2,3,4,5,6,7,8,9,10,
11,12,13,14,15,16,17,
18,19,10]
li[:10]
#=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
-
删除列表中特定索引之前的元素
slice 语法也可以返回一个新的列表,其中包含指定索引之后的值。
li = [1,2,3,4,5,6,7,8,9,10,
11,12,13,14,15,16,17,
18,19,10] 。
li[15:]
#=> [16, 17, 18, 19, 10]
-
删除一个列表中2个索引之间的元素
或者在两个索引之间。
li = [1,2,3,4,5,6,7,8,9,10,
11,12,13,14,15,16,17,
18,19,10] 。
li[12:17]
#=> [13, 14, 15, 16, 17]
-
返回2个索引之间的列表中的每一个第二元素
或者在一个特定的间隔内,在指数之前/之后/之间。
这里我们使用slice语法返回指数10和16之间的每一个第2个值。
li = [1,2,3,4,5,6,7,8,9,10,
11,12,13,14,15,16,17,
18,19,10] 。
li[10:16:2]
#=> [11, 13, 15]
-
对一个整数列表按升序排序 sort()
方法将一个列表突变为升序排列。
li = [10,1,9,2,8,3,7,4,6,5] 。
li.sort()
li #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
-
对一个整数列表按降序排序
通过添加参数reverse=True,也可以用sort()进行降序排序。
li = [10,1,9,2,8,3,7,4,6,5] 。
li.sort(reverse=True)
li #=> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
-
用列表理解法从列表中过滤偶数值
你可以在列表理解中添加条件逻辑,以过滤掉遵循特定模式的值。
这里我们过滤掉能被 2 整除的值。
li = [1,2,3,4,5,6,7,8,9,10]。
[i for i in li if i % 2 != 0]。
#=> [1, 3, 5, 7, 9]
-
计算一个列表中每个值的出现次数
一种选择是迭代一个列表并将计数添加到一个字典中。但是最简单的方法是从 collections 中导入 Counter 类,然后将列表传递给它。
from collections import Counter
li = ['blue', 'pink', 'green',
'green', 'yellow', 'pink', 'orange']
Counter(li)
#=> Counter({'blue': 1, 'pink': 2,
'green': 2, 'yellow': 1, 'orange': 1})
-
从一个列表中的每个嵌套列表中获取第一个元素
列表理解很适合在一个由其他对象组成的列表上进行迭代,并从每个嵌套对象中抓取一个元素。
li = [[1,2,3], [4,5,6],
[7,8,9], [10,11,12],
[13,14,15]]
[i[0]for i in li]
#=> [1, 4, 7, 10, 13]
问题41-50
-
对一个列表来说,插入、查找和删除的时间复杂度是多少?
插入是O(n)
复杂度,如果一个元素被插入到开头,所有其他元素必须向右移动。
通过索引查找是O(1)。但是按值查找是O(n),因为元素需要被迭代直到找到值。
删除是O(n)。如果一个元素在开始时被删除,所有其他的元素必须向左移动。
-
将一个列表中的元素合并成一个字符串。 这可以用 join() 函数来完成。
li = ['The','quick','brown',
'fox', 'jumped', 'over',
'the', 'lazy', 'dog']
' '.join(li)
#=>
'The quick brown fox jumped over the lazy dog'
-
列表乘以一个整数有什么影响?
将一个列表乘以一个整数被称为多重连接,其影响与将一个列表与它本身连接n次是一样的。
下面我们将一个列表乘以5。
['a','b'] * 5
#=>
['a', 'b', 'a', 'b', 'a', 'b', 'a', 'b']
这与以下情况相同
['a','b'] + ['a','b'] + ['a','b'] + ['a','b]]
#=> ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'b']
-
使用 "any "函数,如果列表中的任何值都能被2整除,则返回True
我们可以将any()
与列表理解结合起来,如果返回的列表中的任何值评估为 True,则返回 True。
下面的第一个列表理解返回 True,因为该列表中有一个 2,是可以被 2 整除的。
li1 = [1,2,3]。
li2 = [1,3]
any(i % 2 == 0 for i in li1) #=> True
any(i % 2 == 0 for i in li2) #=> False
-
使用 "all "函数,如果一个列表中的所有值都是负数,则返回 "真 与 any()
函数类似,all()
也可以与列表理解一起使用, 只在返回的列表中的所有值都是True时返回True。
li1 = [2,3,4].
li2 = [2,4]
all(i % 2 == 0 for i in li1) #=> False
all(i % 2 == 0 for i in li2) #=> True
-
你能对一个含有 "None "的列表进行排序吗?
你不能对含有 "无 "的列表进行排序,因为比较运算符(sort()使用的)不能将一个整数与 "无 "进行比较。
li = [10,1,9,2,8,3,7,4,6,None] 。
li.sort()
li #=> TypeError: 'NoneType'和'int'的实例之间不支持'<'
-
列表构造函数会从一个现有的列表中创建什么样的副本?
list 构造函数为传入的 list 创建一个浅层拷贝。也就是说,这比使用 .copy()
更不符合 pythonic。
li1 = ['a','b'] 。
li2 = list(li1)
li2.append('c')
print(li1) #=> ['a', 'b']
print(li2) #=> ['a', 'b', 'c']
-
反转一个列表的顺序
可以用reverse()
方法将一个列表突变为反向顺序。
li = [1,2,3,4,5,6,7,8,9,10] 。
li.reverse()
li #=> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
注意,这是在保持原对象的id()地址不变,改变顺序,而不是返回一个新对象。
-
reverse和reversed之间的区别是什么?
reverse()
将列表原地反转,改变原变量列表的顺序!小心 reversed()
返回列表的一个反向顺序的可迭代对象。
li = [1,2,3,4,5,6,7,8,9,10] 。
list(reversed(li))
#=> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
-
sort和sorted之间的区别是什么?
sort()
在原地修改列表。 sorted()
返回一个相反顺序的新列表。
li = [10,1,9,2,8,3,7,4,6,5]
li.sort()
li #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
li = [10,1,9,2,8,3,7,4,6,5] 。
sorted(li) #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
问题51-60
-
返回一个列表中的最小值 min()
函数返回一个列表中的最小值。
li = [10,1,9,2,8,3,7,4,6,5]
min(li)
#=> 1
-
返回列表中的最大值
max()
函数返回一个列表中的最大值。
li = [10,1,9,2,8,3,7,4,6,5]
max(li)
#=> 10
-
返回一个列表中的数值之和
sum()
函数返回一个列表中所有数值的总和。
li = [10,1,9,2,8,3,7,4,6,5]
sum(li)
#=> 55
-
将一个列表作为一个堆栈
你可以使用append()
和pop()
来把一个列表当作一个堆栈。堆栈的功能是按后进先出(last in first out)
stack = []
stack.append('Jess')
stack.append('Todd')
stack.append('Yuan')
print(stack) #=> ['Jess', 'Todd', ' Yuan']
print(stack.pop()) #=> Yuan
print(stack) #=> ['Jess', 'Todd']
堆栈的一个好处是,可以在O(1)时间内添加和删除元素,因为列表不需要被迭代。
-
寻找2个列表的交集
我们可以通过利用带有set()
来实现这个目的。
li1 = [1,2,3]
li2 = [2,3,4]
set(li1) & set(li2)
#=> {2, 3}
-
找出一个集合和另一个集合的区别
我们不能-
减去列表,但是我们可以减去集合。
li1 = [1,2,3] 。
li2 = [2,3,4]。
set(li1) - set(li2)
#=> {1}
set(li2) - set(li1)
#=> {4}
-
用列表理解法平坦一个列表
与 Ruby 不同,Python3 没有一个明确的 flatten 函数。但是我们可以使用 list comprehension 来平坦一个列表的列表。
li = [[1,2,3],[4,5,6]]
[i for x in li for i in x]
#=> [1, 2, 3, 4, 5, 6]
-
生成一个2个值之间的每个整数的列表
我们可以在2个值之间创建一个范围,然后将其转换为一个列表。
list(range(5,10))
#=> [5, 6, 7, 8, 9]
-
将 2 个列表合并成一个字典
使用 zip()
和 list()
构造函数,我们可以将 2 个列表合并成一个字典,其中一个列表成为键,另一个列表成为值。
name = ['Snowball', 'Chewy', 'Bubbles', 'Gruff']
animal = ['Cat', 'Dog', 'Fish', 'Goat']
dict(zip(name, animal))
-
使用slice语法反转列表的顺序
虽然我们可以用 reverse()
和 reversed()
来反转一个列表,但也可以用 slice 语法来完成。
这将通过从头到尾遍历列表返回一个新的列表。
li = ['a','b',3,4] 。
li[::-1]
#=> [4, 3, 'b', 'a']
结论 在完成这些工作后,我觉得自己更有能力解决算法问题,而不必再猜测具体的列表方法。
消除不断的搜索可以为更高层次的思考释放能量,比如让一个函数做它应该做的事,并降低函数的复杂性。
再次,我希望你也觉得这很有用。
作者介绍