查看原文
其他

Python 教学 | 列表推导式 & 字典推导式

专注python教学的 数据Seminar 2023-08-12


Python教学专栏,旨在为初学者提供系统、全面的Python编程学习体验。通过逐步讲解Python基础语言和编程逻辑,结合实操案例,让小白也能轻松搞懂Python!
>>>点击此处查看往期Python教学内容

本文目录

一、前言

二、列表推导式

  (一)一行代码创建列表

  (二)列表推导式中的条件语句

三、字典推导式

  (一)一行代码创建字典

  (二)根据旧字典生成新字典

    (三)字典推导式中的条件语句

四、总结

五、Python教程

本文共8226个字,阅读大约需要21分钟,欢迎指正!

Part1前言

列表和字典是 Python 中常用的两种数据结构,列表是一个有序的序列,列表中的所有元素都存放在[]内,不同元素之间使用逗号分隔,访问列表中的元素通过索引来实现,而字典是一个无序的键值对集合,字典中的元素都存放在{}内,元素是以'键':'值'的形式存储,需要通过字典的键来访问字典的值。

在往期教程中我们已经介绍了创建列表和字典的常用方法,具体内容可以回顾这两篇文章:

以上文章中的方法更适合创建一些内容比较简单、可以人工写入元素的列表或者字典,如果需要创建的元素比较复杂(比如需要使用循环和条件语句来生成)时,这种传统的方式会让代码看起来冗余。而推导式的优点就是提供了一种简明的列表(字典)创建方法,这样使得我们可以在一行代码中完成循环、筛选等操作,很大程度地减少了代码量。此外,掌握推导式的另一个重要原因是,除了自己的需求之外,我们不可避免地会阅读别人的代码,所以从这点来看,学习推导式也是必要的。

下面我们给出一个列表推导式的简单使用场景:在进行文本分析中的文本分词时,将分词结果保存在名AllWords的列表中,停用词保存在名为Stopwords的列表中,现在我们想要去除分词结果中的停用词和长度为 1 的词。为了更直观地看到推导式的优点,我们分别采取使用/不使用推导式两种方式实现这个目标。

# 先定义相关变量,与是否使用推导式无关
#分词结果
AllWords = ['加入','企研','·','社科','大','数据','平台','会员',',','用','最','独家','的','数据',',','学','最','实用','的','Python',',','画','最酷','的','图','!']
#停用词
Stopwords = ['·',',''用','最']

# 一、使用for循环
Words = []
for word in AllWords:
    if word not in Stopwords and len(word) > 1:
        Words.append(word)
# ['加入', '企研', '社科', '数据', '平台', '会员', '独家', '数据', '实用', 'Python', '最酷']

# 二、使用列表推导式
Words = [word for word in AllWords if word not in Stopwords and len(word) > 1]
# 结果同上

从上面的结果可以看到相比于显式的循环,推导式的表达更为简洁,同时在处理大量数据集时,推导式也能够更快地完成任务。本期文章我们将详细介绍推导式中最常见的列表推导式和字典推导式。

本教程基于 pandas 1.5.3 版本书写。本文中所有 Python 代码均在集成开发环境 Visual Studio Code (VScode) 中使用交互式开发环境 Jupyter Notebook 中编写,本文分享的代码请使用 Jupyter Notebook 打开。

Part2列表推导式

推导式的用法有很多,本文在这里不会一 一介绍,我们主要介绍使用推导式生成简单列表 / 字典以及生成带有条件语句的列表 / 字典。在正式介绍推导式的用法之前,我们需要先掌握其基本语法。

list = [expression for member in iterable (if conditional)]

上述表达式中主要包括expressionmemberiterableconditional四个部分,其中:

  • expression可以是任何一个能够返回值的合法表达式,这个值就是创建的列表中的元素值
  • iterable是可迭代对象,每次循环会得到一个值也就是member
  • conditional是可选的条件语句,其允许列表推导式选择性地保留符合要求的值,(if conditional)也可以放在expression之后,能够根据条件生成列表元素。

我们用一句话来描述列表推导式,就是从可迭代对象中循环取出数据值,并将这个值根据表达式处理成一个元素,最后存放在列表中,最后列表推导式的结果就是包含所有处理后元素的列表。

在 Python 中,iterable 是指可迭代对象,也就是可以使用循环遍历获取其中的元素的对象,常见的可迭代对象包括字符串、列表、元组、字典、集合等等。

1一行代码创建列表

现在我们来看一下列表推导式的用法,通过上文中的例子可以看到,使用 for 循环创建列表的步骤可以总结为以下三步:

  1. 创建一个空的list对象
  2. 循环遍历可迭代对象iterable,每次取出其中的一个值
  3. 将循环取出的值根据表达式处理,然后将得到的值作为列表中的元素写入空的list

如何将 for 循环改写为列表推导式呢?其实并不难,我们只要将循环语句块中的表达式放在推导式的最前面,然后依次书写循环的其他部分即可。另外注意一点,使用列表推导式就可以直接在列表中创建元素而不需要先创建一个空列表,因此在改写时不用加append()函数。下面我们介绍一个使用列表推导式改写 for 循环的例子,假设现在我们需要获取某个专利(该专利涉及多个专利分类号,不同分类号以;间隔)的专利分类号的大组(指分类号斜杠前的部分),代码如下:

# 假设该专利分类号为:A01B02/00;A01B02/10;A01B20/20
FLH = 'A01B02/00;A01B02/10;A01B20/20'

# 一、使用 for 循环
FLH_LIST = []
for one_flh in FLH.split(';'):
    flh_zu = one_flh.split('/')[0]
    FLH_LIST.append(flh_zu)
FLH_LIST
# 输出结果:['A01B02', 'A01B02', 'A01B20']

# 二、使用列表推导式改写
FLH_LIST = [one_flh.split('/')[0for one_flh in FLH.split(';')]
FLH_LIST
# 结果同上

在上面的代码中,可迭代对象(即 for 循环的循环体)是FLH.split(';')(为一个列表),one_flh则在这个列表中循环取值;循环语块的表达式是one_flh.split('/')[0],其含义是获取每个专利分类号的/左边的部分,改写为推导式时,该表达式放在了推导式最前面,于是每次循环得到的值经过表达式的计算得到最终值,保存在列表中。

这个示例中我们只对一个专利的分类号进行了处理,而实际中我们需要对千万级别的专利数据进行相同的处理,这时我们就可以将推导式封装在自定义函数中,然后使用函数apply()map()对数据进行批量操作。

2列表推导式中的条件语句

上节介绍的替代 for 循环创建简单的列表是列表推导式最常见的用法之一,这种用法适用于处理数值、字符串等基本数据类型,特别是在需要快速生成一组初始值的情况下非常实用。此外,列表推导式还有另一个常见用法,它允许根据特定规则筛选出满足条件的元素,使得列表推导式更加灵活,实现该用法只需要在列表推导式中使用条件语句即可。

现在我们回顾一下前言中的例子:

#分词结果
AllWords = ['加入','企研','·','社科','大','数据','平台','会员',',','用','最','独家','的','数据',',','学','最','实用','的','Python',',','画','最酷','的','图','!']
Stopwords = ['·',',''用','最']  #停用词

# 一、使用for循环
Words = []
for word in AllWords:
    if word not in Stopwords and len(word) > 1:
        Words.append(word)
Words
# ['加入', '企研', '社科', '数据', '平台', '会员', '独家', '数据', '实用', 'Python', '最酷']

# 二、使用列表推导式
Words = [word for word in AllWords if word not in Stopwords and len(word) > 1]
Words  # 结果同上

在这个例子中,我们使用了条件语句if word not in Stopwords and len(word) > 1,这个条件语句的作用是对循环的输出值进行筛选,只保留了非停用词且长度大于 1 的值。需要注意一点,条件判断语句可以是任何合法的逻辑表达式,当条件语句较长或者判断条件较复杂时,可以先将条件判断语句封装在函数中,再将函数作为推导式的条件语句。现在假设在上述例子中,我们需要筛选分词的条件是:非停用词、长度大于 1 且必须是中文字符,得到修改后的代码如下:

AllWords = ['加入','企研','·','社科','大','数据','平台','会员',',','用','最','独家','的','数据',',','学','最','实用','的','Python',',','画','最酷','的','图','!']
Stopwords = ['·',',''用','最']

# 定义用于筛选的条件函数
def condition(String):
    return String not in Stopwords and len(String) > 1 and bool(re.search("[\u4e00-\u9fa5]", String))
Words = [word for word in AllWords if condition(word)]
Words
# ['加入', '企研', '社科', '数据', '平台', '会员', '独家', '数据', '实用', '最酷']

可以看到在上面的代码中,封装着条件语句的条件函数写在推导式的最右边,实现了根据我们给定的条件筛选循环输出值的作用。所以当条件判断语句较为简短时,我们可以直接将其写在推导式中,而当语句较为复杂时,我们也可以直接将封装了条件语句的函数写在推导式中,这足以体现出使用列表推导式的灵活性。

到目前为止,我们在列表推导式中所涉及的条件判断语句是只有if语句的情况,而实际中条件判断的结构还有如包含else语句if...else...等等(是一个合法的条件判断语句即可),比如当我们的条件判断语句是if...else...形式时,就需要根据不同的判断条件来确定列表中的元素,这种情况下列表推导式的书写也有一些变化,为了便于理解,我们通过一个简单的例子看一下。假设现在有一组数值型数据,我们想要对这组数据中的正数开根号、负数取绝对值,代码如下:

# 初始的数值型数据
OriginList = [2.3-46.67.2-3.7-1.1]

List = [round(i**0.52if i > 0 else abs(i) for i in OriginList]
List
# [1.52, 4, 2.57, 2.68, 3.7, 1.1]

这个示例中的条件判断语句是if i > 0 else abs(i),可以看到与只有if语句的情况不同,此时推导式中的条件语句放在了循环语句的前面,这是因为在每次的循环迭代时需要根据条件选择不同的表达式。这个条件语句的含义为当循环输出值为正数时按照expression计算(在本例中为开根号,并且保留两位精度),反之则按照 else后的表达式计算(在本例中为取绝对值)。

Part3字典推导式

字典推导式和列表推导式在逻辑上是类似的,它们都可以结合循环和条件语句来使用。不同的是字典推导式返回的是一个字典,其基础语法与列表推导式略有不同:

dict = {key_expression:value_expression for key, value in iterable (if conditional)}

语法中的key_expressionvalue_expression分别为字典的键和值的表达式,与列表推导式相同,iterable是一个可迭代对象,key, value取出每次循环中的键和值,一般情况下使用函数items()zip()使得每次循环返回一对值,分别作为键和值;(if conditional)是字典推导式中的条件判断语句,该语句不仅可以放在循环语句的后面,也可以放在表达式key_expressionvalue_expression之后用于修改循环的返回值,语法如下:

dict = {key_expression:value_expression1 (if conditional else value_expression2) for key, value in iterable}

上述条件语句表示:当满足if语句的条件时,循环返回的key, value经过表达式计算后构成的键-值对key_expression:value_expression1作为字典的元素,反之,构成的键-值对key_expression:value_expression2作为字典的元素。

1一行代码创建字典

话不多说,下面我们通过一个例子来说明字典推导式的用法。假设现在我们在进行文本分析,此时需要对句子分词并生成词语和词性的对照字典,使用 for 循环实现的代码如下:

from jieba import posseg

Text = "开启全面建设社会主义现代化国家新征程"
# 分词并得到词性
res = posseg.lcut(Text)

Word_flag = {}   # 创建空字典,用于存放元素
for word, flag in res:
    Word_flag[word] = flag
Word_flag
# {'开启': 'v','全面': 'n','建设': 'vn','社会主义': 'n','现代化': 'vn','国家': 'n','新': 'a','征程': 'n'}

上面代码中的函数posseg.lcut()得到一个列表,其中的元素类型是pair(指一个元组,包含分词及其对应词性)。我们现在使用字典推导式来实现这个需求,代码如下:

Text = "开启全面建设社会主义现代化国家新征程"
res = posseg.lcut(Text)
Word_flag = {word:key for word, key in res}
Word_flag  # 结果同上

2根据旧字典生成新字典

我们还可以使用字典推导式对原字典进行修改,从而得到新字典。比如我们可以将上个例子中的分词的词性修改为对应中文名称,代码如下:

dict_old = Word_flag  # 复制一份数据,重命名
# 修改原字典的值,将英文词性修改为中文词性
En_Ch = {'v':'动词''n':'名词''vn':'名动词''a':'形容词'}
dict_new = {key:En_Ch[value] for key, value in dict_old.items()}
dict_new

在上面代码中,我们使用函数items()来循环调用原字典中的键-值对,通过灵活定义字典推导式中的表达式 key_expressionvalue_expression可以将原字典的键和值做相应的修改,从而形成新的键-值对。

3字典推导式中的条件语句

在介绍字典推导式语法时我们就提到过,字典推导式中的条件判断语句不仅可以放在末尾来筛选循环的返回值,也可以放在表达式key_expressionvalue_expression之后根据条件选择不同的表达式来修改循环的返回值。下面我们分别介绍这两种用法:

我们仍然使用上文中的分词&词性字典作为初始字典,假设现在我们只想要得到词性为名词的分词&词性字典,实现代码如下:

# 复制一份原字典,重命名
dict_old = Word_flag
# 筛选循环的值,提取词性为名词的分词
dict_new = {key:value for key, value in dict_old.items() if value == 'n'}
dict_new
# {'全面': 'n', '社会主义': 'n', '国家': 'n', '征程': 'n'}

在这个例子中我们通过if条件语句对循环的值进行了筛选,只有当值value=='n'时,循环的值才会以键值对形式存在字典中。如果现在我们只想要知道分词的词性是否为名词,也就是重新得到一个新字典,此时字典的键还是初始字典中的分词结果,但是当初始字典中分词的词性为'n'时,新字典的值为“名词”,否则字典的值为“非名词”,实现代码如下:

# 复制一份原字典,重命名
dict_old = Word_flag
# 为键值对设置不同值
dict_new = {key:"名词" if value == "n" else "非名词" for key, value in dict_old.items()}
dict_new
# {'开启': '非名词','全面': '名词','建设': '非名词','社会主义': '名词','现代化': '非名词','国家': '名词','新': '非名词','征程': '名词'}

Part4总结

本文介绍的列表推导式和字典推导式提供了一种简洁灵活、富有 Python 风格的列表 / 字典的构建方法。与使用传统的 for 循环和条件语句相比,推导式可以使代码更加易读易理解、并且也能够节省时间。不过虽然推导式是一种强大的工具,但并不能说使用推导式创建列表和字典是最优解,没有任何一种放之四海而皆准的方法,在一些逻辑比较复杂的情况下(比如使用嵌套推导式)可能会牺牲代码的可读性,因此在实际操作时我们应该根据自己的需求对各种方法做取舍。

Part5Python 教程

 目录



星标⭐我们不迷路!想要文章及时到,文末“在看”少不了!

点击搜索你感兴趣的内容吧

往期推荐


Python 教学 | Pandas 时间数据处理方法

Python 教学 | Pandas 函数应用(apply/map)【下】

Python 教学 | Pandas 函数应用(apply/map)【上】

Python 教学 | Pandas 数据匹配(含实操案例)

Python 教学 | Pandas 数据合并(含目录文件合并案例)




数据Seminar




这里是大数据、分析技术与学术研究的三叉路口


文 | 《社科领域大数据治理实务手册》


    欢迎扫描👇二维码添加关注    

点击下方“阅读全文”了解更多

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存