python基础-操作列表和迭代器

推荐 6287次阅读 134人点赞 作者: WuBin 发布时间: 2024-06-13 13:26:27
扫码到手机查看

数组基本操作

定义多个空列表

dates, highs, lows = [], [], []

创建数组并执行循环

arr = ['wu', 'bin', 'work']

for item in arr:
    print(item)

range

range(min默认0, max必须指定,step默认1)

# range() 函数示例
for i in range(5):  # 相当于 range(0, 5, 1)
    print(i)  # 输出 0 1 2 3 4

创建数字数组range

for value in range(1, 10):
    print(value);
注意这里生成的数字是1-9,没有10

特别注意:

print( list( range(1,6) ) );

这里生成的结果是:[1, 2, 3, 4, 5],如果要生成1-6,那么:

print( list( range(1, 6+1) ) );
所以要谨记,使用range(min, max)生成数值的时候,如果要包含max,就必须给max + 1

如果要将创建的数字转化为数字列表(数组)

number=list(range(1,6))
print(number)

输出:[1,2,3,4,5]

range第三个参数还可以指定步长

range(2, 11, 2) // [2, , 6, 8, 10]
在python中,**表示乘方,number**2,表示Number二次方,2**3,表示2的立方
arr = []
for item in arr:
   val = item**2
   arr.append(val)

当然也有简便的方法

for item in arr:
   arr.append( item**2 )

还可以求数组中最大值、最小值、数字和

arr = [1, 2, 3, 4]
min(arr)
max(arr)
sum(arr)

列表解析(列表推导式

[表达式 for 变量 in 可迭代对象 if 条件]

列表推导式。本页可搜:列表推导式

arrs = [value**2 for value in range(1, 11)]
print(arrs);

value**2是表达式,代表平方,比如:

value = 3 
value = value**2
print(value) // 9

for value in range(1,11):for循环为表达式提供值

最终arrs输出:[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

其他例子:

# 生成一个包含 0 到 9 中偶数的平方的列表
even_squares = [x**2 for x in range(10) if x % 2 == 0]
print(even_squares)
# 从一个字符串列表中筛选出长度大于 3 的字符串
words = ["apple", "cat", "banana", "dog"]
long_words = [word for word in words if len(word) > 3]
print(long_words)

解包

在 Python 中,当你有一个可迭代对象(像列表、元组之类的),并且知晓该可迭代对象的元素数量时,就能使用解包赋值把可迭代对象中的元素分别赋值给不同的变量。解包赋值的基本语法为:

var1, var2, ..., varN = iterable

这里的iterable是一个可迭代对象,var1varN是要接收可迭代对象中元素的变量,而且变量的数量要和可迭代对象中元素的数量一致。

arr = [(1, 'a'), (2, 'b')]
arr1, arr2 = arr

最终,arr1的值是(1, 'a')arr2的值是(2, 'b')

应用示例:

# 交换变量值
a = 1
b = 2
a, b = b, a

通过解包赋值轻松实现了ab值的交换。

接收函数返回的多个值:
def get_name_and_age():
    return "Alice", 25

name, age = get_name_and_age()

这个例子中,函数get_name_and_age返回一个包含姓名和年龄的元组,使用解包赋值可以把元组中的元素分别赋值给nameage变量。

在 Python 里,当在return语句中使用逗号分隔多个值时,即使没有显式地使用括号,Python 也会将这些值封装成一个元组返回。也就是说,return "Alice", 25return ("Alice", 25)这两种写法在功能上是等价的,最终返回的都是一个包含"Alice"25这两个元素的元组。

解包运算符 *

  • 在函数调用时解包参数*可以把列表、元组或其他可迭代对象解包成单独的参数。
def add_numbers(a, b, c):
    return a + b + c

numbers = [1, 2, 3]
result = add_numbers(*numbers)
print(result)  

上面这个就是将列表中的元素拆开,分别当作单独的参数传入。

再看一个复杂点的例子:

xy_map = [
    [1, 10],
    [2, 20],
    [3, 30],
    [4, 40]
]

# 使用 zip(*xy_map) 对 xy_map 进行解包和重新组合
# *xy_map 会将 xy_map 中的子列表解包,然后 zip 函数将这些解包后的元素重新组合成元组
# 最后 [*zip(*xy_map)] 将这些元组放入一个列表中
x_unique, y_mean = [*zip(*xy_map)]

# 打印结果
print("x 唯一值:", x_unique)
print("y 均值:", y_mean)

代码解释:

  • xy_map是一个包含多个子列表的列表,每个子列表包含一个x值和对应的y均值。
  • zip(*xy_map)xy_map中的子列表解包(xy_map解包后是[1, 10], [2, 20], [3, 30], [4, 40]),然后zip将对应位置的元素组合成元组。然后再返回一个迭代器,这个迭代器中包含了两个元组 <迭代器 (1,2,3,4) (10,20,30,40)  /迭代器>
  • *zip(*xy_map) 是 对上一步中zip返回的迭代器再进行解包,比如到这一步 可以使用两个参数进行解包接收,比如 x,y=*zip(*xy_map),这样x=(1,2,3,4),y=(10,20,30,40)。
  • [*zip(*xy_map)]*操作符用于对zip(*xy_map)生成的迭代器进行解包。解包后,迭代器中的元组会被依次放入列表中,最终得到一个包含这些元组的列表[(1, 2, 3, 4), (10, 20, 30, 40)]。。
  • x_unique, y_mean = [*zip(*xy_map)]把列表中的两个元组分别赋值给x_uniquey_mean

输出结果:

x 唯一值: (1, 2, 3, 4)
y 均值: (10, 20, 30, 40)

切片

处理列表的部分元素,叫做切片。

基本概念

切片操作符的一般形式是[start:stop:step]

  • start是切片的起始位置(包含该位置的元素),如果省略,则默认从列表的开头开始。
  • stop是切片的结束位置(不包含该位置的元素),如果省略,则默认到列表的末尾结束。
  • step是切片的步长,即每隔多少个元素取一个。比如[::20]中,step的值为20,意味从0开始切片直到数组末尾,且着每隔20个元素取一个。
在切片操作中,start默认值是0,表示从序列的开头开始切片;stop的默认值是序列的长度,表示切片到序列的末尾;step的默认值是1,表示每次切片的步长为1,即逐个元素进行切片。注意,切片操作中,步长不能为 0,否则会引发ValueError异常。
my_list = [1, 2, 3, 4, 5]
print(my_list[::])  # 输出 [1, 2, 3, 4, 5],相当于从开头到末尾,步长为1进行切片

取出出列表中前3个元素 start=0 stop=3 step=1

arr = [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
print( arr[0:3] )
// [1, 4, 9]

如果是第2~4元素

print(arrs[1:4])

如果没有指定第一个索引,那么默认从0开始

print(arrs[:3])

如果从第三个元素开始~末尾,那么可以省略末尾索引

print(arrs[3:])

负数索引则从列表末尾开始计算,如下返回最后3个元素

print(arrs[-3:])

结果:[64, 81, 100]

获取列表长度

len(arrs)

切片之后的也是一个数组,也能遍历

for value in arr[:3]:
    print(value)

复制列表,同时省略起止索引,[:]

arr_copy = arrs[:]
注意!arrs=arr_copy 这不是复制!!这只是让arr_copy关联到arrs指向的列表,所以这样做只是让两个变量指向同一个列表!
arr_same = arrs;
arrs.append(199)
print(arr_same) // arr_same 最后一个元素是199

使用切片反转列表

切片操作的语法是[start:stop:step],当step为负数时,表示从后往前取值,利用这个特性可以实现列表的反转。

my_list=[1,2,3,4,5]reversed_list=my_list[::-1]print(reversed_list)

my_list[::-1]表示从列表的开头到末尾,以步长为-1进行切片

结果:[5,4,3,2,1]

元组

列表是可以修改的,而不可以被修改的列表叫做元组。

定义元组,要使用括号()

dimensions = (200, 50)
dimensions[0] // 读取使用
dimensions[1]

修改元组中的数据会报错

dimensions[0] = 20
// TypeError: 'tuple' object does not support item assignment

元组也可以使用for循环

dimensions = (1, 2)
for item in dimensions:
    print(item)

虽然不能改变元组中的值,但是可以改变存储元组值的变量

dimensions = (1, 2)
dimensions = (3, 4 , 5) // 不会报错

列表常用方法

列表长度len

len()是 Python 内置函数,专门用于返回对象的长度或元素个数,对于列表而言,它可以快速准确地返回列表中元素的数量。

my_list = [1, 2, 3, 4, 5]
length = len(my_list)
print(f"列表的长度是: {length}")

index查找某个元素在列表中首次出现的位置

sequence.index(value, start=0, end=len(sequence))
  • sequence:这是要操作的序列对象,例如列表、元组等。
  • value:代表要查找的元素。
  • start:是可选参数,指定开始查找的索引位置,默认值为 0,也就是从序列的起始位置开始查找。
  • end:同样是可选参数,指定查找结束的索引位置,默认值是序列的长度,意味着查找至序列的末尾。
fruits = ["apple", "banana", "cherry", "banana"]
# 查找 "banana" 首次出现的索引
index = fruits.index("banana")
print(index)

fruits.index("banana")会查找"banana"fruits列表中首次出现的索引,输出结果为1

指定查找范围:

fruits = ["apple", "banana", "cherry", "banana"] // 元组也是一样
# 从索引 2 开始查找 "banana"
index = fruits.index("banana", 2)
print(index)

指定从索引 2 开始查找"banana",输出结果为3

异常处理

要查找的元素不在序列中,index()方法会抛出ValueError异常。

fruits = ["apple", "banana", "cherry"]
try:
    index = fruits.index("date")
    print(index)
except ValueError:
    print("元素未找到")

统计指定元素出现次数count

count方法可以用来统计列表中指定元素出现的次数。

numbers = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
# 统计数字 2 出现的次数
count_2 = numbers.count(2)
print(f"数字 2 在列表中出现的次数: {count_2}")
  • numbers.count(2):统计列表numbers中元素2出现的次数,结果为2。代表出现了两次。

元组是 Python 中的不可变序列,同样可以使用count方法来统计元素出现的次数。

fruits = ('apple', 'banana', 'apple', 'cherry', 'apple')
# 统计 'apple' 出现的次数
count_apple = fruits.count('apple')
print(f"'apple' 在元组中出现的次数: {count_apple}")
  • fruits.count('apple'):统计元组fruits中元素'apple'出现的次数,结果为3

字符串也是一种序列类型,count方法可以用来统计字符串中某个子字符串出现的次数。

sentence = "Hello, hello, world!"
# 统计 'hello' 出现的次数(区分大小写)
count_hello = sentence.count('hello')
print(f"'hello' 在字符串中出现的次数: {count_hello}")

# 统计 'o' 出现的次数
count_o = sentence.count('o')
print(f"'o' 在字符串中出现的次数: {count_o}")
  • sentence.count('hello'):统计字符串sentence中子字符串'hello'出现的次数,结果为1注意,该方法区分大小写,所以'Hello'不会被统计在内。
  • sentence.count('o'):统计字符串sentence中字符'o'出现的次数,结果为2

enumerate获取列表中每个元素值与索引值

alist = ['a', 'b', 'c']
for index, item in enumerate(alist):
    print(index, item)

最终执行结果:

0 a
1 b
2 c

enumerate(arr):可以获取列表中每个元素的索引 和它对应的值。

zip

zip()是一个内置函数,其主要功能是将多个可迭代对象(像列表、元组、字符串等)中的对应元素打包成一个个元组,接着返回由这些元组构成的迭代器。

基本用法

zip()函数的基本语法为zip(*iterables),其中*iterables表示接收任意数量的可迭代对象作为参数。

# 定义两个列表
list1 = [1, 2, 3]
list2 = ['a', 'b', 'c']

# 使用 zip 函数将两个列表打包
zipped = zip(list1, list2)

# 将 zip 对象转换为列表,方便查看结果
result = list(zipped)
print(result)

在上述代码中,zip(list1, list2)会把list1list2中对应位置的元素打包成元组,最终得到[(1, 'a'), (2, 'b'), (3, 'c')]

处理不同长度的可迭代对象

当传入的可迭代对象长度不一致时,zip()函数会以最短的可迭代对象为基准进行打包,多余的元素会被忽略。示例如下:

list1 = [1, 2, 3]
list2 = ['a', 'b']

zipped = zip(list1, list2)
result = list(zipped)
print(result)

list2的长度比list1短,因此zip()函数只会打包前两个元素,结果为[(1, 'a'), (2, 'b')]

*运算符结合使用

zip()函数可以和*运算符配合使用来实现解包操作。

zipped = [(1, 'a'), (2, 'b'), (3, 'c')]

# 使用 * 运算符解包
unzipped = zip(*zipped)

# 将解包后的结果转换为列表
list1, list2 = map(list, unzipped)
print(list1)
print(list2)

在上述代码中,zip(*zipped)zipped进行解包操作,将元组中的元素重新拆分成两个列表,最终输出[1, 2, 3]['a', 'b', 'c']

map

map()是一个内置函数,它的主要作用是将一个函数应用到可迭代对象(如列表、元组等)的每个元素上,并返回一个包含结果的迭代器。

map(function, iterable, ...)
  • function:这是一个需要应用到可迭代对象每个元素上的函数。
  • iterable:表示一个或多个可迭代对象,map()会依次将function应用到这些可迭代对象的对应元素上。如果提供了多个可迭代对象,function必须能够接受与可迭代对象数量相同的参数。

使用map()函数将列表中的每个元素都乘以 2:

# 定义一个函数,用于将输入的数字乘以 2
def multiply_by_two(x):
    return x * 2

# 定义一个列表
numbers = [1, 2, 3, 4, 5]

# 使用 map() 函数将 multiply_by_two 应用到 numbers 列表的每个元素上
result = map(multiply_by_two, numbers)

# 将 map 对象转换为列表,方便查看结果
result_list = list(result)
print(result_list)

multiply_by_two函数应用到numbers列表的每个元素上,最终返回一个包含结果的迭代器,将迭代器转化为list后打印,得到[2, 4, 6, 8, 10]

处理多个可迭代对象

# 定义一个函数,用于将两个数字相加
def add(x, y):
    return x + y

# 定义两个列表
list1 = [1, 2, 3]
list2 = [4, 5, 6]

# 使用 map() 函数将 add 函数应用到两个列表的对应元素上
result = map(add, list1, list2)

# 将 map 对象转换为列表
result_list = list(result)
print(result_list)

add函数应用到list1list2对应位置的元素上,这里是让让list1[0]+list2[0],list1[1]+list2[1]等,最终得到[5, 7, 9]

lambda函数结合使用

通常,我们会使用lambda函数(匿名函数)与map()函数结合。

# 使用 lambda 函数和 map() 实现元素乘以 2
numbers = [1, 2, 3, 4, 5]
result = map(lambda x: x * 2, numbers)
print(list(result))

# 使用 lambda 函数和 map() 实现两个列表对应元素相加
list1 = [1, 2, 3]
list2 = [4, 5, 6]
result = map(lambda x, y: x + y, list1, list2)
print(list(result))

map()返回的是一个迭代器,通常需要将其转换为列表或其他序列类型才能查看完整的结果。

sorted排序

sorted()是一个内置函数,对可迭代对象(像列表、元组、字符串等)进行排序操作,并且会返回一个新的已排序列表,而不会改变原始的可迭代对象。

sorted(iterable, key=None, reverse=False)
  • iterable:这是必须提供的参数,代表要进行排序的可迭代对象,比如列表、元组、字符串等。
  • key:这是一个可选参数,是一个函数,它会作用于可迭代对象中的每个元素,排序时会依据这个函数的返回值来进行。默认值为None,表示直接比较元素本身。
  • reverse:同样是可选参数,是一个布尔值。若设置为True,则进行降序排序;若为False(默认值),则进行升序排序。
numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
sorted_numbers = sorted(numbers)
print(sorted_numbers)

sorted(numbers)会对numbers列表进行升序排序,最终输出[1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]

numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
sorted_numbers = sorted(numbers, reverse=True)
print(sorted_numbers)

numbers列表进行降序排序,输出结果为[9, 6, 5, 5, 5, 4, 3, 3, 2, 1, 1]

words = ["apple", "banana", "cherry", "date"]
# 按照字符串的长度进行排序
sorted_words = sorted(words, key=len)
print(sorted_words)

key=len表示按照字符串的长度来排序,最终输出['date', 'apple', 'banana', 'cherry']

students = [("Alice", 25), ("Bob", 20), ("Charlie", 22)]
# 按照年龄进行排序
sorted_students = sorted(students, key=lambda x: x[1])
print(sorted_students)

lambda函数lambda x: x[1]作为key参数,意味着按照元组的第二个元素(年龄)进行排序,输出结果为[('Bob', 20), ('Charlie', 22), ('Alice', 25)]

也可以对 可迭代对象(列表、元组)进行排序:

data = [(1, 3), (2, 2), (1, 2)]
sortedList = sorted(data)
print(sortedList)

当使用sorted()函数对可迭代对象进行排序,且不传入key参数时,默认会按照元素的字典序进行排序。

对于由zip函数创建的可迭代对象,其元素是元组。在这种情况下,排序首先比较元组的第一个元素,如果相等,则继续比较第二个元素,以此类推,直到找到不同的元素或到达元组的末尾。

上述代码中,sorted函数会先比较每个元组的第一个元素,所以(1, 3)(1, 2)会排在(2, 2)前面,而对于(1, 3)(1, 2),由于第一个元素相同,会继续比较第二个元素,从而确定它们的顺序。最终结果为[(1, 2), (1, 3), (2, 2)]

当然也可以使用匿名函数进行指定,按照指定的位数进行排序:

data = [(1, 3), (2, 2), (1, 2)]

# 按照元组的第一个元素升序排序
sorted_by_first = sorted(data, key=lambda x: x[0])
print(sorted_by_first)

# 按照元组的第二个元素升序排序
sorted_by_second = sorted(data, key=lambda x: x[1])
print(sorted_by_second)

上述代码中,sorted(data, key=lambda x: x[0])表示按照元组的第一个元素进行升序排序,sorted(data, key=lambda x: x[1])表示按照元组的第二个元素进行升序排序。key参数是一个函数,它指定了排序的依据。

sum求总和

sum()是一个内置函数,主要用于计算可迭代对象(像列表、元组、集合等)中所有元素的总和。

sum(iterable, start=0)
  • iterable:这是一个可迭代对象,比如列表、元组、集合等,其元素应当是可以进行加法运算的类型,像数字类型。
  • start:这是一个可选参数,代表求和的起始值,默认值为 0。
# 对列表中的数字求和
numbers = [1, 2, 3, 4, 5]
result = sum(numbers)
print(result)
# 指定起始值求和
numbers = [1, 2, 3, 4, 5]
result = sum(numbers, 10)
print(result)

将起始值设定为 10,所以sum()函数会先把列表中的元素相加得到 15,再加上起始值 10,最终输出结果 25。

注意:

  • iterable里的元素必须是能够进行加法运算的类型,不然会引发类型错误。
  • 对于浮点数求和,要留意浮点数精度问题。要是需要高精度计算,可以考虑使用decimal模块。

标准库数组工具

groupby

from itertools import groupby

py标准库中模块itertools的函数groupby,可将迭代器中相邻的重复元素分组。来看一个简单的例子:

from itertools import groupby

# 定义一个已排序的列表
data = [1, 1, 2, 2, 2, 3, 4, 4]

# 使用 groupby 进行分组
for key, group in groupby(data):
    print(f"Key: {key}, Group: {list(group)}")

运行结果:

Key: 1, Group: [1, 1]
Key: 2, Group: [2, 2, 2]
Key: 3, Group: [3]
Key: 4, Group: [4, 4]

groupby函数把相邻的相同元素划分成组,key代表每组的元素值,group是一个迭代器,它包含了该组的所有元素。

再考虑一下,在使用 groupby 函数时,如何保留分组的索引信息?

from itertools import groupby

# 示例数据
data = ['a', 'a', 'b', 'b', 'b', 'c', 'd', 'd']

# 为每个元素添加索引
indexed_data = enumerate(data)
"""
print(list(indexed_data))
# 得到 [(0, 'a'), (1, 'a'), (2, 'b'), (3, 'b'), (4, 'b'), (5, 'c'), (6, 'd'), (7, 'd')]
"""

# 使用 groupby 进行分组
groups = []
for key, group in groupby(indexed_data, key=lambda x: x[1]):
    """
    print(f"分组依据的值(key): {key}")
    print(list(group))
    分组依据的值(key): a
    [(0, 'a'), (1, 'a')]
    分组依据的值(key): b
    [(2, 'b'), (3, 'b'), (4, 'b')]
    分组依据的值(key): c
    [(5, 'c')]
    分组依据的值(key): d
    [(6, 'd'), (7, 'd')]
    """

    indices = [index for index, _ in group]
    groups.append((key, indices))

# 输出分组结果及索引
for key, indices in groups:
    print(f"Key: {key}, Indices: {indices}")    

indexed_data =enumerate(data)

enumerate是一个内置函数,其作用是为可迭代对象(像列表、元组、字符串等)添加索引。enumerate函数会返回一个枚举对象,此对象包含了一系列的元组,每个元组由索引和对应的元素值构成。这个对象里的每个元组包含两个元素:第一个元素是索引(从 0 开始),第二个元素是data列表中对应位置的元素。

转化为列表,最终打印出:[(0, 'a'), (1, 'a'), (2, 'b'), (3, 'b'), (4, 'b'), (5, 'c'), (6, 'd'), (7, 'd')] 

groupby(indexed_data, key=lambda x: x[1])

1、key

for后面的key:表示当前分组的依据,也就是lambda函数针对分组内元素执行后所返回的值 这里是x:x[1],以第一个为例就是'a'。

groupby函数里,key=参数的作用是指定一个回调函数(这里是指定匿名函数lambda),该函数会对数组中每个元素执行,然后依据处理结果进行分组,分组后的结果存入for .. group in .. 的group中。

2、lambda

lambda x: x[1]是一个回调函数!,用于指定对indexed_data中的每个元素执行的函数!最终的结果赋予for key...的key!

lambda函数简介

lambda函数也被称作匿名函数,它是一种小型的、临时的、一次性使用的函数。与常规函数不同,lambda函数不需要使用def关键字来定义,也没有函数名。其语法格式为:

lambda 参数列表: 表达式

lambda函数会接收参数列表中的参数,然后对表达式进行求值,并返回表达式的结果。

对于lambda x: x[1]

  • x代表indexed_data中的每个元素。由于indexed_data是通过enumerate(data)得到的,所以x是一个包含索引和元素值的元组,例如(0, 1)(1, 1)等。
  • x[1]表示取元组x的第二个元素,也等价于原始数据列表data中的元素值。
  • lambda x: x[1]这个匿名函数的功能是接收一个元组x,(这个x就是遍历的indexed_data中的每一个元素)然后返回该元组的第二个元素(比如 (0, ‘a’) 就返回'a'),因此每轮循环,将得到的比如'a',赋值给for后面的Key。然后groupby就会根据这个key (‘a’)进行分组,并将分组结果保存到for 后面的group中。

3、indices = [index for index, _ in group]

这里使用了列表推导式(list comprehension)。其目的是从group这个迭代器中提取出所有元素的索引,然后将这些索引存储在一个新的列表indices中。

这里我们挑一轮遍历得到的group,[(2, 'b'), (3, 'b'), (4, 'b')] 为例:

列表推导式简介

列表推导式是 Python 中一种简洁且高效的创建列表的方式,它允许你通过一种紧凑的语法来生成列表。其基本语法如下:

[表达式 for 变量 in 可迭代对象 if 条件]

其中,表达式是对每个元素进行处理的表达式,变量是从可迭代对象中取出的元素,if 条件是可选的过滤条件。

  • index, _:这是元组解包(tuple unpacking)的操作。index用于接收元组中的第一个元素,也就是索引;_是 Python 中的一个惯用符号,通常用于表示一个临时变量,在这里它接收元组中的第二个元素(即值),但由于我们只关心索引,所以使用_来忽略这个值。
  • [index for index, _ in group]:这是一个列表推导式,它会遍历group中的每个元组,将元组中的索引提取出来(分别遍历 【index是2,_是'b'】【index是3,_是'b'】【index是4,_是'b'】 ),并将这些结果中的index(索引)依次添加到一个新的列表中。最终,这个新列表会被赋值给变量indices
indices = [index for index, _ in group]
print(f"得到的indices: {indices}")

遍历打印得到的结果:

得到的indices: [0, 1]
得到的indices: [2, 3, 4]
得到的indices: [5]
得到的indices: [6, 7]

如果是这样写:

indices = [_ for index, _ in group]
print(f"得到的indices: {indices}")
得到的indices: ['a', 'a']
得到的indices: ['b', 'b', 'b']
得到的indices: ['c']
得到的indices: ['d', 'd']

最终for key, indices in groups:该函数执行的结果:

Key: a, Indices: [0, 1]
Key: b, Indices: [2, 3, 4]
Key: c, Indices: [5]
Key: d, Indices: [6, 7]

实现了将每个值进行分组,并且获取了原本该值对应的索引。

tee

itertools.tee是 Python 标准库itertools模块中的一个函数,其主要作用是将一个迭代器复制成多个独立的迭代器,这些新生成的迭代器可以独立地对原迭代器的数据进行遍历,而不会相互干扰。

itertools.tee(iterable, n=2)
  • iterable:这是必需的参数,代表要进行复制的原始迭代器或者可迭代对象,例如列表、元组、生成器等。
  • n:该参数是可选的,默认值为 2。它表示要生成的独立迭代器的数量。
  • 返回值:tee函数会返回一个包含n个独立迭代器的元组。这些迭代器可以独立地对原迭代器的数据进行遍历。

比如我有下面这段代码:

sortedList = [(1, 5383), (1, 5566), (1, 5648),... (2, 8206), (3, 6437),.. (9, 28208),.. (11, 65458), (11, 65583)]
for x,y in groupby( sortedList, key=lambda _:_[0] ):
    print('依据分组的x', x); # x是1...3..9..11
    print('分组的结果y', list(y)) # y正常是 所有的[(1, 5383)] ,所有的 [(11, 65458), (11, 65583)] 这些,分别找出x是1..9..11的集合
    # 仅仅获取 元组中 5383 5566... 65583 这样的 第二个值 组成一个列表
    y_list = [v for _, v in y]

这里会发现,程序会报错,提示y不能用了。

这是因为在打印的时候,输出了 list(y),迭代器就会被耗尽,后续就无法再次使用它。若你想打印y的副本,同时又不影响后续操作,可借助itertools.tee函数创建迭代器的副本。

我们可以这样修改:

from itertools import groupby, tee
for x, y in groupby(sortedList, key=lambda _: _[0]):
        # 创建迭代器的副本
        y_copy, y_original = tee(y)

        # x就是lambda匿名函数执行结果,返回 每个元组中的第一个值
        print('依据分组的x', x)
        # y就是根据返回的值,进行分组的结果
        print('分组的结果y', list(y_copy))
        y_list = [v for _, v in y_original]
tee函数返回的是一个包含多个迭代器的元组,而非迭代器的副本内容。要打印副本内容,需要对tee返回的迭代器进行解包,然后将其中一个迭代器转换为列表来打印。tee(要复制的迭代器,复制的数量=2【默认是2】),所以对结果要进行解包。
ycopy = tee(y) 错!!
y_copy, y_original = tee(y) 对
  1. tee函数使用y_copy, y_original = tee(y)tee返回的元组解包成两个迭代器,y_copy用于打印,y_original用于后续操作。

再看个单独使用的简单的例子:

from itertools import tee

# 创建一个简单的可迭代对象
numbers = [1, 2, 3, 4, 5]

# 使用 tee 函数复制迭代器,默认生成 2 个迭代器
iter1, iter2 = tee(numbers)

# 遍历第一个迭代器
print("第一个迭代器的元素:")
for num in iter1:
    print(num)

# 遍历第二个迭代器
print("第二个迭代器的元素:")
for num in iter2:
    print(num)

首先创建了一个列表numbers。接着使用tee函数将这个列表的迭代器复制成两个独立的迭代器iter1iter2。之后分别对这两个迭代器进行遍历,会发现它们都能完整地输出原列表中的元素。

注意事项

  • 内存消耗:由于tee函数需要使用缓冲队列来存储数据,若原迭代器的数据量非常大,或者生成的迭代器数量过多,可能会导致较高的内存消耗。
  • 原迭代器的使用:一旦使用tee函数复制了迭代器,就不应该再直接使用原迭代器,否则可能会导致数据不一致的问题。
  • 迭代器耗尽:每个新生成的迭代器都是独立的,当其中一个迭代器耗尽时,不会影响其他迭代器的使用。不过,所有迭代器共享同一个缓冲队列,若某个迭代器消耗数据的速度远快于其他迭代器,可能会导致缓冲队列占用大量内存。
点赞 支持一下 觉得不错?客官您就稍微鼓励一下吧!
关键词:python列表
推荐阅读
  • python基础-操作列表和迭代器

    python基础笔记-操作列表和迭代器的相关方法

    6287次阅读 134人点赞 发布时间: 2024-06-13 13:26:27 立即查看
  • uniapp实现被浏览器唤起的功能

    当用户打开h5链接时候,点击打开app若用户在已经安装过app的情况下直接打开app,若未安装过跳到应用市场下载安装这个功能在实现上主要分为两种场景,从普通浏览器唤醒以及从微信唤醒。

    10665次阅读 707人点赞 发布时间: 2022-12-14 16:34:53 立即查看
  • PHP

    【正则】一些常用的正则表达式总结

    在日常开发中,正则表达式是非常有用的,正则表达式在每个语言中都是可以使用的,他就跟JSON一样,是通用的。了解一些常用的正则表达式,能大大提高你的工作效率。

    14674次阅读 580人点赞 发布时间: 2021-10-09 15:58:58 立即查看
  • 【中文】免费可商用字体下载与考证

    65款免费、可商用、无任何限制中文字体打包下载,这些字体都是经过长期验证,经得住市场考验的,让您规避被无良厂商起诉的风险。

    13541次阅读 1092人点赞 发布时间: 2021-07-05 15:28:45 立即查看
  • Vue

    Vue3开发一个v-loading的自定义指令

    在vue3中实现一个自定义的指令,有助于我们简化开发,简化复用,通过一个指令的调用即可实现一些可高度复用的交互。

    17567次阅读 1415人点赞 发布时间: 2021-07-02 15:58:35 立即查看
  • JS

    关于手机上滚动穿透问题的解决

    当页面出现浮层的时候,滑动浮层的内容,正常情况下预期应该是浮层下边的内容不会滚动;然而事实并非如此。在PC上使用css即可解决,但是在手机端,情况就变的比较复杂,就需要禁止触摸事件才可以。

    15926次阅读 1286人点赞 发布时间: 2021-05-31 09:25:50 立即查看
  • Vue

    Vue+html2canvas截图空白的问题

    在使用vue做信网单页专题时,有海报生成的功能,这里推荐2个插件:一个是html2canvas,构造好DOM然后转canvas进行截图;另外使用vue-canvas-poster(这个截止到2021年3月...

    31325次阅读 2469人点赞 发布时间: 2021-03-02 09:04:51 立即查看
  • Vue

    vue-router4过度动画无效解决方案

    在初次使用vue3+vue-router4时候,先后遇到了过度动画transition进入和退出分别无效的情况,搜遍百度没没找到合适解决方法,包括vue-route4有一些API都进行了变化,以前的一些操...

    27411次阅读 2114人点赞 发布时间: 2021-02-23 13:37:20 立即查看
交流 收藏 目录