python推导式是一种简洁且强大的内建语法结构,它允许我们以一种极其紧凑和易于理解的方式创建新的列表、字典、集合或生成器对象,能够更高效地操作和转换数据结构。
列表推导式基本语法如下图:
其他推导式的语法也基本相似,看着有点lambda的感觉,上面图片遍历一个名为oldlist的列表,并将大于0的元素逐一进行双倍运算后,添加至新列表newlist中。接下来我们逐一分析各个推导式:
目录
一、列表推导式
二、推导式中使用函数解决复杂情况
1、表达式较复杂
2、推导条件较复杂
三、推导式中的嵌套表达式(了解写法即可)
四、字典推导式
五、集合推导式
六、元组推导式(生成器表达式)
一、列表推导式
通过一行代码即可从一个已存在的可迭代对象(如列表、元组或其他序列)中生成一个新的列表,接着用前面的例子来讲:
#方法一
oldlist=[-1,0,-3,1,2,3]
newlist=[]
for i in oldlist:
if i > 0:
newlist.append(i*2)
print(newlist)
#方法二
newlist = [i * 2 for i in oldlist if i > 0]
print(newlist)
#同样处理列表,使用了python推导式后,代码省了不少,简洁明了。
再来看一个使用列表推导式对数据进行过滤和转换的实例:
#设我们有一个包含用户信息的列表,每个元素是一个字典,表示每位用户的属性(如姓名、年龄和邮箱地址)。
# 现在我们需要创建一个新的列表,只包含年龄大于20岁的用户,并将他们的邮箱地址全部转换为小写。
users = [
{'name': 'Alice', 'age': 20, 'email': 'Alice@gmail.com'},
{'name': 'yang', 'age': 18, 'email': 'Yang@gmail.com'},
{'name': 'Bob', 'age': 21, 'email': 'BOB@gmail.com'},
{'name': 'Eve', 'age': 24, 'email': 'EvE@gmail.com'}
]
new_users = [{'name':user['name'],'age':user['age'],'email':user['email'].lower()}
for user in users if user['age']>20]
print(new_users)
在上述代码中,列表推导式 [{"name": user["name"], "email": user["email"].lower()} for user in users if user["age"] > 20] 的作用是:
1、遍历 users 中的每个 user 字典。
2、使用条件 if user["age"] > 20 筛选出年龄大于20岁的用户。
3、对于满足条件的用户,创建一个新的字典,其中 "name" 键的值保持不变,而 "email" 键的值通过调用 .lower() 方法转换为小写。
运行这段代码后,new_users 将会是一个新的列表,包含如下内容:
[{'name': 'Bob', 'age': 21, 'email': 'bob@gmail.com'},
{'name': 'Eve', 'age': 24, 'email': 'eve@gmail.com'}]
上面的例子条件语句都是在推导式结尾,我们再来看看一种条件语句在表达式中的例子:
#我们有个列表,里面有字符串,也有数字,现在将数字转换为字符串,将字符串转换为大写
oldlist = [1, 'a', 2, 'b', 3, 'c']
newlist = [str(i) if isinstance(i, int) else i.upper() for i in oldlist]
print(newlist)
通过上述例子,我们可以总结一下:
1、推导式从一个可枚举数据(列表,元组,集合,字典等)推导出一个列表。
2、推导式可以加推导条件,只对符合条件的元素推导
3、要推导出的元素使用表达式生成,可以用if else生成不同元素(处理生成推导结果的过程较复杂的情况)
二、推导式中使用函数解决复杂情况
如果表达式或者推导条件特别复杂怎么办?可以使用函数,例子如下:
1、表达式较复杂
#例1、假设我们有一个用户列表,除了年龄和邮箱地址之外,还包括用户的注册日期(格式为字符串),
#现在我们需要筛选出年龄大于20岁的用户,并将他们的邮箱地址转换为小写,同时计算他们注册至今的天数。
# 原始用户信息列表,添加了注册日期字段
users = [
{'name': 'Alice', 'age': 20, 'email': 'Alice@gmail.com',"reg_date": "2018-01-01"},
{'name': 'yang', 'age': 18, 'email': 'Yang@gmail.com',"reg_date": "2017-01-01"},
{'name': 'Bob', 'age': 21, 'email': 'BOB@gmail.com',"reg_date": "2015-01-01"},
{'name': 'Eve', 'age': 24, 'email': 'EvE@gmail.com',"reg_date": "2023-01-01"}
]
# 自定义函数,用于计算从注册日期到现在的天数
def days_since_reg(date_string):
reg_date = datetime.strptime(date_string, "%Y-%m-%d")
today = datetime.now()
return (today - reg_date).days
# 利用列表推导式结合自定义函数进行筛选、转换和计算
filtered_users = [{"name": user["name"],"email": user["email"].lower(),"days_since_reg": days_since_reg(user["reg_date"])}
for user in users if user["age"] > 20]
print(filtered_users)
''' 输出
[{'name': 'Bob', 'email': 'bob@gmail.com', 'days_since_reg': 3357}, {'name': 'Eve', 'email': 'eve@gmail.com', 'days_since_reg': 435}]
'''
# 例2、推导1949年到2024年的所有年份,标记出闰年和平年
def run_year(year):
if year % 4 == 0 and year % 100 != 0 or year % 400 == 0:
return True
else:
return False
yearlist = [f'润年{year}' if run_year(year) else f'平年{year}' for year in range(1949, 2025)]
print(yearlist)
''' 输出
['平年1949', '平年1950', '平年1951', '润年1952', '平年1953', '平年1954', '平年1955', '润年1956',
'平年1957', '平年1958', '平年1959', '润年1960', '平年1961', '平年1962', '平年1963', '润年1964',
'平年1965', '平年1966', '平年1967', '润年1968', '平年1969', '平年1970', '平年1971', '润年1972',
'平年1973', '平年1974', '平年1975', '润年1976', '平年1977', '平年1978', '平年1979', '润年1980',
'平年1981', '平年1982', '平年1983', '润年1984', '平年1985', '平年1986', '平年1987', '润年1988',
'平年1989', '平年1990', '平年1991', '润年1992', '平年1993', '平年1994', '平年1995', '润年1996',
'平年1997', '平年1998', '平年1999', '润年2000', '平年2001', '平年2002', '平年2003', '润年2004',
'平年2005', '平年2006', '平年2007', '润年2008', '平年2009', '平年2010', '平年2011', '润年2012',
'平年2013', '平年2014', '平年2015', '润年2016', '平年2017', '平年2018', '平年2019', '润年2020',
'平年2021', '平年2022', '平年2023', '润年2024']
'''
2、推导条件较复杂
直接上例子:
#推导1~10中的所有质数
# 定义函数判断一个数是否是质数
def is_prime(num):
if num == 1:
return False
for i in range(2, num):
if (num % i) == 0:
return False
else:
return True
p_nums = [i for i in range(1, 10) if is_prime(i)] #推导条件使用定义函数
print(p_nums) #输出[2, 3, 5, 7]
三、推导式中的嵌套表达式(了解写法即可)
用例子演示:
# 假设有一个二维坐标点的列表,如果x和y不相等,则将(x, y)作为一个点添加到新列表中
list1=[(x,y) for x in range(1,3) for y in range(1,4) if x!=y]
print(list1) #输出[(1, 2), (1, 3), (2, 1), (2, 3)]
#相当于:
list_n=[]
for x in range(1,3):
for y in range(1,4):
if x!=y:
list_n.append((x,y))
print(list_n)
有两层循环的相对来说下面的写法会更易懂一点,不建议写嵌套,我们知道写法即可
四、字典推导式
推导字典的方式和推导列表很相似,区别在:1、使用大括号 2、使用键值对。看例子:
# 1、将键值对列表转换为字典
key_value = [('name', 'Harry'), ('age', 18)]
# 使用字典推导式创建字典
dc = {k: v for k, v in key_value}
print(dc) # 输出:{'name': 'Harry', 'age': 18}
# 2、基于另一个字典生成新字典,同时修改值:
original_dict = {'麦叔': 59, '张三': 87, 'FGA': 78, '石石': 100, '莫名': 90}
# 创建一个新字典,其中每个值增加10岁
new_dict = {k: v + 10 for k, v in original_dict.items()}
print(new_dict)
# 3、过滤字典,只保留满足条件的键值对:
scores = {'麦叔': 59, '张三': 87, 'FGA': 78, '石石': 100, '莫名': 90}
# 只保留分数在90分以上的同学信息
high_scores = {k: v for k, v in scores.items() if v >= 90}
print(high_scores)
# 4、将两个相同键的字典合并,合并时按值相加:
dict1 = {'a': 1, 'b': 2}
dict2 = {'a': 3, 'b': 4, 'c': 5}
# 合并两个字典,相同键的值相加
merged_dict = {k: dict1.get(k, 0) + dict2.get(k, 0) for k in set(dict1.keys()).union(dict2.keys())}
print(merged_dict)
五、集合推导式
推导集合的方式和列表是一样的,区别在于:1、使用大括号,类似于推导字典,但它是单个元素,而不是键值对 2、集合会自动过滤掉重复的元素。
#1、生成包含偶数的集合:
numbers = [1, 2, 3, 4, 5, 6]
even_numbers=[x for x in numbers if x%2==0]
print(even_numbers) # 输出:[2, 4, 6]
#2、基于列表中的元素计算平方得到新的集合:
list_num=[1, 2, 3, 4, 5, 6]
squares = {x**2 for x in list_num}
print(squares) #{1, 4, 36, 9, 16, 25}
六、元组推导式(生成器表达式)
元组推导式和列表推导式的用法也完全相同,只是元组推导式是用 ( ) 圆括号将各部分括起来,而列表推导式用的是中括号 [ ],另外元组推导式返回的结果是一个生成器对象。
#1:创建一个包含数字平方的元组
tuple1=(i**2 for i in range(1,5))
print(tuple1) # <generator object <genexpr> at 0x0000020101BA5E00>
print(next(tuple1)) # 输出:1
print(tuple(tuple1)) # 输出:(4, 9, 16)
#2:从另一个序列中筛选元素并转换为元组
words = ['apple', 'banana', 'cherry', 'date', 'elderberry']
long_words = tuple(word for word in words if len(word) > 5)
print(long_words) # 输出: ('banana', 'cherry', 'elderberry')
#3:将两个列表一一对应组合成元组,并合并为一个元组序列
list1 = [1, 2, 3]
list2 = ['a', 'b', 'c']
paired_tuples = tuple((x, y) for x, y in zip(list1, list2))
print(paired_tuples) # 输出: ((1, 'a'), (2, 'b'), (3, 'c'))
'''
注意,在实际应用中,由于元组是不可变的数据结构,
因此通常我们会利用其不可变性来存储一次性生成的结果或者作为临时的数据结构。
在上述例子中,如果需要多次迭代结果,可能更适合用列表推导式,
但若只遍历一次或空间效率要求较高时,元组推导式更为适合。
'''
在Python中,如果列表推导式需要处理大量数据并且生成的中间结果占用巨大内存时,可以考虑使用生成器表达式来替代。生成器表达式不会一次性将所有结果存储到内存中,而是按需产生结果,这对于处理大数据流非常有效。
# 假设有一个巨大的数字序列,我们想计算每个数的平方但内存有限
# huge_numbers = [x**2 for x in range(10**10)]
# 这段代码很可能会卡死电脑,不建议运行,除非你的电脑是超级计算机。因为它要在内存中做100亿次计算,然后保存这100亿个数字。
# 生成器表达式方式:
huge_squares = (x**2 for x in range(10**10))
print(huge_squares) #输出 <generator object <genexpr> at 0x0000020101BA5E00>
print(next(huge_squares)) #输出 0
print(next(huge_squares)) #输出 1
print(next(huge_squares)) #输出 4
print(next(huge_squares)) #输出 9
#这是一个生成器,它不会一次性生成100亿个数字,只有调用next()的时候,
#它才会生成一个新的,返回给你。也就是说,同一个时间,只保存一个数字。
#每次调用 next() 都会计算并返回生成器的下一个元素,直到生成器中的元素被全部遍历完。
在这个例子中,如果我们用列表推导式一次性计算range(10**10)
中所有整数的平方并存储起来,将会消耗大量内存。而使用生成器表达式,则可以逐个生成并处理这些平方值,从而有效地控制内存使用量。生成器在python中是一种非常实用和高效的工具,它的功能有点像一个“懒惰的计算器”,只计算你当下需要的结果,而不是一次性算出所有结果并存储起来。下次单独更新关于生成器迭代器等处理序列数据的工具。
综上所述,总结如下
推导式的优点:
1、代码简洁,易于阅读和理解,能够以一行表达式的形式实现复杂的数据转换逻辑。
2、内存高效,尤其是生成器表达式(元组/字典推导式在创建时占用空间,但生成器仅在迭代时产生值)。
推导式的缺点:
1、如果需要多次迭代或修改结果,由于列表推导式生成的是不可变序列(元组推导式)或一次性创建所有元素(列表推导式),它们不如普通循环加append灵活。
2、对于非常大的数据集,可能会造成内存压力,尤其是在不恰当使用列表推导式一次性生成全部结果时。
常用场景:
1、数据清洗和转换:如筛选、映射、组合等操作,例如从一个列表中筛选出满足特定条件的元素并转换为新的格式。
2、创建新序列:基于现有序列快速生成一个新的有序集合,如计算数字序列的平方、立方或者将两个序列配对生成元组。
3、函数参数传递:用于生成函数所需的参数序列,比如用在
zip()
、map()
或其他高阶函数中。
希望以上内容能帮助到大家合理运用python的推导式~