本文主要介绍Python的高级特性:列表推导式、迭代器和生成器,是面试中经常会被问到的特性。因为生成器实现了迭代器协议,可由列表推导式来生成,所有,这三个概念作为一章来介绍,是最便于大家理解的,现在看不懂没关系,下面我不仅是会让大家知其然,重要的更是要知其所以然。

列表推导式

前几天有个HR让我谈谈列表推导式,我说这我经常用,就是用旧的列表生成一个新的列表的公式,他直接就把我拒了,让我回去复习一下,挺受打击的,所以决定也帮助大家回顾一下。

内容

  • 列表推导式:旧的列表->新的列表
  • 了解:字典推导式 集合推导式

1.列表推导式:

格式 [表达式 for 变量 in 旧列表]

[表达式 for 变量 in 旧列表 if 条件]

例1:生成名字长度大于3且首字母大写的新列表。

names_old = ['tom', 'amy', 'daming', 'lingling']
names_new = [name.capitalize() for name in names_old if len(name) > 3]
print(names_new)
复制代码

输出:

['Daming', 'Lingling']
复制代码

例2: (大厂初级笔试题目)生成一个元组列表,要求每个元素为(0-5偶数, 0-10奇数)形式。输出结果为:

[(0, 1), (0, 3), (0, 5), (0, 7), (0, 9), (2, 1), (2, 3), (2, 5), (2, 7), (2, 9), (4, 1), (4, 3), (4, 5), (4, 7), (4, 9)]
复制代码

for循环实现代码:

new_list = list()
for i in range(5): # 偶数
if i % 2 == 0:
for j in range(10): # 奇数
if j % 2 != 0:
new_list.append((i, j))
复制代码

列表推导式代码:

new_list = [(i, j) for i in range(5) for j in range(10) if i % 2 == 0 and j % 2 != 0]
复制代码

例3:(大厂初级笔试题目)给出一个员工列表:

employees_old = [{'name': "tmo", "salary": 4800},
{'name': "amy", "salary": 3800},
{'name': "daming", "salary": 7000},
{'name': "lingling", "salary": 5600}]
复制代码

如果员工薪资大于5000则加200,否则加500,输出新的员工列表。

列表推导式:

employees_new = [employee['salary'] + 200 if employee['salary'] > 5000 else employee['salary'] + 500 for employee in employees_old]
print(employees_new)
复制代码

输出:

[5300, 4300, 7200, 5800]
复制代码

发现结果是员工薪资列表,回过头看一下代码,确实是把得到的数字给了列表,那要返回员工列表要怎么实现呢?让我们用普通for循环的方式来进行一下对比:

for employee in employees_old:
if employee['salary'] > 5000:
employee['salary'] += 200
else:
employee['salary'] += 500 print(employees_old)
复制代码

输出:

[{'name': 'tmo', 'salary': 5300}, {'name': 'amy', 'salary': 4300}, {'name': 'daming', 'salary': 7200}, {'name': 'lingling', 'salary': 5800}]
复制代码

没错,我们注意到两者的差别了,列表推导式我们少了一步赋值(在字典元素上进行赋值),不能直接返回一个薪资数值而是一个员工字典给列表。正确的列表推导式如下:

employees_new = [
{'name': employee['name'], 'salary': employee['salary'] + 200} if employee['salary'] > 5000 else
{'name': employee['name'], 'salary': employee['salary'] + 500} for employee in employees_old] print(employees_new)
复制代码

2.字典推导式:

例1:

dict_old = {'a': 'A', 'b': 'B', 'c': 'C', 'd': 'C'}
dict_new = {value: key for key, value in dict_old.items()}
print(dict_new)
复制代码

输出:

{'A': 'a', 'B': 'b', 'C': 'd'}
复制代码

3.集合推导式:

类似列表推导式 典型用法:去重

例1:

list_old = [1, 2, 3, 5, 2, 3]
set_new = {x for x in list_old}
print(set_new)
复制代码

输出:

{1, 2 ,3, 5}
复制代码

小结:

到目前为止,列表推导式不就是一个用来创建列表的式子么?除了可以简化代码,装装X?其实,列表推导式还有另一个优点是相比于for循环更高效,因为列表推导式在执行时调用的是Python的底层C代码,而for循环则是用Python代码来执行。嗷~面试官最想听到的,是第二点。

迭代器

由于迭代器协议对很多人来说,是一个较为抽象的概念,而且生成器自动实现了迭代器协议,所以我们需要先讲解一下迭代器协议的概念,也是为了更好的理解接下来的生成器。

概念

可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator

迭代是访问集合元素的一种方式,迭代器是一个可以记住遍历位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有元素被访问完结束。

迭代器只能往前不能后退。

  • 迭代器协议:是指对象需要提供__next__()方法,它要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代。
  • 可迭代对象:就是实现了迭代器协议的对象。

举个例子,对Python稍微熟悉一点的朋友应该知道,Python的for循环不但可以用来遍历list,还可以用来遍历文件对象,如下所示:

with open('F:/test/test.txt') as f:
for line in f:
print(line)
复制代码

为什么在Python中,文件还可以使用for循环进行遍历呢?这是因为,在Python中,文件对象实现了迭代器协议,for循环并不知道它遍历的是一个文件对象,它只管使用迭代器协议访问对象即可。正是由于Python的文件对象实现了迭代器协议,我们才得以使用如此方便的方式访问文件,如下所示:

with open('F:/test/test.txt') as f:
print(dir(f))
复制代码

输出:

['__class__', '__del__', '__dict__', '__dir__', '__init__', '__iter__', '__next__', 'closed', 'line_buffering', 'newlines', 'read', 'readline'......]
复制代码

可迭代的是不是肯定就是迭代器?

  • 生成器是可迭代的,也是迭代器。
  • list是可迭代的,但不是迭代器。list可以借助iter()函数将可迭代的变成迭代器list->iter(list)->迭代器next()

可迭代对象:

  1. 生成器
  2. 元组 列表 集合 字典 字符串

如何判断一个对象是否是可迭代?

借助isinstance()函数:

from collections import Iterable

print(isinstance([x for x in range(10)], Iterable))  # 列表
print(isinstance('hello world', Iterable)) # 字符串
print(isinstance(100, Iterable)) # 数字
print(isinstance((x for x in range(10)), Iterable)) # 迭代器
复制代码

输出:

True
True
False
True
复制代码

生成器

生成器是Python最有用的特性之一,也是使用的最不广泛的Python特性之一。究其原因,主要是因为,在其他主流语言里面没有生成器的概念。正是由于生成器是一个“新”的东西,所以,它一方面没有引起广大工程师的重视,另一方面,也增加了工程师的学习成本,最终导致大家错过了Python中如此有用的一个特性。

概念

我们已经知道,通过列表推导式可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面那几个元素,那后面绝大多数元素占用的空间都白白浪费了。所以,如果列表元素可以按照某种算法在循环的过程中不断推算出后续的元素,这样既不必创建完整的list,从而还可以节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator

Python使用生成器对延迟操作提供了支持。所谓延迟操作,是指在需要的时候才产生结果,而不是立即产生结果。这也是生成器的主要好处。

定义生成器

Python有两种不同的方式提供生成器:

方法一:借助列表推导式

生成器表达式:类似于列表推导(这也就是为什么第一节我要先介绍列表推导式),但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表。

例1:

my_generator = (x for x in range(5))  # 注意是()不是[]
print(my_generator) # 发现不能打印出元素
print(type(my_generator))
print(my_generator.__next__()) # 三种得到元素的方法,注意看输出结果
print(next(my_generator))
for i in my_generator:
print(i) # 注意会抛出StopIteration异常
# print(next(my_generator))
print(next(my_generator)) # generator只能遍历一次
复制代码

输出:

Traceback (most recent call last):
File "E:/pycharm/Leetcode/RL_Learning/printdata.py", line 11, in <module>
print(next(my_generator))
StopIteration
<generator object <genexpr> at 0x0000000000513660>
<class 'generator'>
0
1
2
3
4
复制代码

方法二:借助函数

生成器函数:使用yield语句而不是return语句返回函数结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,起到暂停的作用,以便下次从它离开的地方继续执行。

步骤:

  1. 定义函数,函数返回使用yield关键字;
  2. 调用函数,接收函数返回值;
  3. 得到的返回结果就是生成器;
  4. 借助next()__nest__()得到想要的元素。

例2:你的函数里面只要出现了yield关键字,你的函数就不再是函数了,就变成生成器了:

# 斐波那契数列:
def fib(length): # 1. 定义函数
a, b = 0, 1
n = 0
while n < length:
n += 1
yield b # return b + 暂停
a, b = b, a + b g = fib(5) # 2. 调用函数
print(g) # 3. 返回的就是生成器
print(next(g)) # 4. 借助`next()`或`__nest__()`得到想要的元素
print(next(g)) # 每调用一次产生一个值
print(next(g))
print(g.__next__())
print(g.__next__())
复制代码

输出:

<generator object fib at 0x0000000001DDDFC0>
1
1
2
3
5
复制代码

注意:生成器只能遍历一次。

当调用函数的时候,并没有进函数进行执行,而是直接生成一个生成器,当调用next的时候,才进入函数真正开始执行,除了第一次调用next()方法是从函数头开始执行,其余每次都是接着从上次执行到yield的地方接着执行的。

小结:

使用生成器以后,代码行数更少。大家要记住,如果想把代码写的Pythonic,在保证代码可读性的前提下,代码行数越少越好。

合理使用生成器,能够有效提高代码可读性。只要大家完全接受了生成器的概念,理解了yield语句和return语句一样,也是返回一个值。那么,就能够理解为什么使用生成器比不使用生成器要好,能够理解使用生成器真的可以让代码变得清晰易懂。

在实际工作中,充分利用Python生成器,不但能够减少内存使用,还能够提高代码可读性。掌握生成器也是Python高手的标配。
如果本文对你有帮助,不要忘记关注点赞或收藏支持一下~

Python中的”黑魔法“与”骚操作“的更多相关文章

  1. 【Python从入门到精通】(九)Python中字符串的各种骚操作你已经烂熟于心了么?

    您好,我是码农飞哥,感谢您阅读本文,欢迎一键三连哦. 本文将重点介绍Python字符串的各种常用方法,字符串是实际开发中经常用到的,所有熟练的掌握它的各种用法显得尤为重要. 干货满满,建议收藏,欢迎大 ...

  2. C#中的9个“黑魔法”与“骚操作”

    C#中的9个"黑魔法"与"骚操作" 我们知道C#是非常先进的语言,因为是它很有远见的"语法糖".这些"语法糖"有时过于好 ...

  3. Python中的文件和目录操作实现

    Python中的文件和目录操作实现 对于文件和目录的处理,虽然可以通过操作系统命令来完成,但是Python语言为了便于开发人员以编程的方式处理相关工作,提供了许多处理文件和目录的内置函数.重要的是,这 ...

  4. Python中关于csv的简单操作

    Python中关于csv的简单操作 CSV操作简单,直接import csv即可, 主要使用reader和pandas 1 reader的简单使用 csv.reader("1.csv&quo ...

  5. Python中json的简单读写操作

    Python中json的简单读写操作 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.它基于ECMAScript的一个子集. JSON采用完全独立于语言的 ...

  6. Python 中的类的相关操作

    构造函数 构造函数是任何类都有的特殊方法.当要创建一个类时,就要调用构造函数.他的名字是__init__.init的前后分别是两个下划线.时间类Time的构造函数如下: >>> cl ...

  7. Python 中包/模块的 `import` 操作

    版权声明:博客为作者原创,允许转载,但必须注明原文地址: https://www.cnblogs.com/byronxie/p/10745292.html 用实例来说明 import 的作用吧. 创建 ...

  8. python中 对文件的读写操作 以及如何边写入 边保存flush()

    转自:https://blog.csdn.net/t8116189520/article/details/78854708 首先 python中打开文件大致常用的几类如下: 1.写入文件write # ...

  9. Python中字符串有哪些常用操作?纯干货超详细

随机推荐

  1. python中操作excel数据

    python操作excel,python有提供库 本文介绍openpyxl,他只支持新型的excell( xlsx)格式,读取速度还可以 1.安装 pip install openpyxl 2.使用 ...

  2. kafka对接Rancher日志

    kafka对接Rancher日志 目录 kafka对接Rancher日志 概述 环境准备 正常对接kafka集群 1.helm添加bitnami库 2.下载 kafka 对应的chart压缩文件 3. ...

  3. Mac 安装Homebrew慢的问题解决

    一开始安装,在官网上的命令: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/ma ...

  4. 蚂蚁上市员工人均一套大 House,阿里程序员身价和这匹配吗?

    作者 | 硬核云顶宫 责编 | 伍杏玲 出品 | CSDN(ID:CSDNnews) 上周,蚂蚁集团迎来IPO,其发行价格将达到68.8元,总市值将突破2万亿元.市场对蚂蚁的成长性有着充分的信心,为了 ...

  5. npm中的命令指令的参数的 简写介绍

    在使用npm时,使用的的缩写 install: 缩写为i,表示安装. --global: 缩写为-g,表示:全局标识,可以在任意目录中使用该工具.全局安装. --save: 缩写为-S,表示安装的包将 ...

  6. python:列表的去重:两种方法的问题是:结果是没有保持原来的顺序。

    列表的去重 1.使用set的特型,python的set和其他语言类似, 是一个无序不重复元素集 orgList = [1,0,3,7,7,5] #list()方法是把字符串str或元组转成数组 for ...

  7. LeetCode 043 Multiply Strings

    题目要求:Multiply Strings Given two numbers represented as strings, return multiplication of the numbers ...

  8. PyQt(Python+Qt)学习随笔:Qt Designer中主窗口对象的animated属性

    animated属性用于设置在操作可浮动部件和工具栏时是否设置动画. 当一个可浮动部件或工具栏被拖到主窗口上时,主窗口将调整其内容,以显示浮动部件或工具栏应该放置的位置.设置此属性后主窗口将使用平滑动 ...

  9. 手把手教你爬取B站弹幕!

    效果 输入要爬取的视频的BV号即可爬取该视频的弹幕. 过程 基本思路 基本的思路很简单,还是老步骤: 1.构造爬取的url 2.解析返回的数据 3.使用json或Xpath或正则表达式提取数据 4.保 ...

  10. OutputFormat---自定义输出方式

    简介 可以自定义输出的格式和文件,例如包含某字段的输出到一个指定文件,不包含某字段的输出到另一个文件. 案例 数据 www.nevesettle.com www.baidu.com www.qq.co ...