讲解一下Python 推导式

Python 推导式是一种独特的数据处理方式,可以从一个数据序列构建另一个新的数据序列的结构体,下面为大家讲解一下Python 推导式使用方法。

1. 推导式分类与用法

1.1 列表推导

列表推导式是一种快速生成列表的方式。它一般用“[]”括起来,例如

>>> [i for i in range(10)]
[0,1, 2, 3, 4, 5, 6, 7, 8, 9]

这是一种最基本的用法,列表推导式先执行for循环,再把遍历的元素(或者对元素的一些计算表达式)作为列表的元素返回一个列表。

>>> [i*i for i in range(10)]
[0,1, 4, 9, 16, 25, 36, 49, 64, 81]

它就相当于

>>> l = []
>>> for i in range(10):
...   l.append(i*i)
...
>>>            

我们可以用列表推导快速初始化一个二维数组

m = [[0,0,0],
    [0,0,0],
    [0,0,0]
    ]

n = []
for row in range(3):
   r = []
   for col in range(3):
       r.append(0)
   n.append(r)
print(n)

用下面的式子就可以得到这个二维数组

>>> [[0]*3 for i in range(3)]
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]

列表推导式有很多种形式

for循环前面加if…else…

这种生成的元素个数不会少,只是根据for循环的结果使用不同的表达式

# 如果i是5的倍数,结果是i,否则就是0
>>> [i if i % 5 == 0 else 0 for i in range(20)]
[0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 10, 0, 0, 0, 0, 15, 0, 0, 0, 0]

# 如果是偶数就加100,奇数就减100
>>> [i+100 if i % 2 == 0 else i-100 for i in range(10)]
[100, -99, 102, -97, 104, -95, 106, -93, 108, -91]

for循环后面加if…

这种会只取符合条件的元素,所以元素个数跟条件相关

# for循环的结果只选择是偶数的
>>> [i for i in range(10) if i % 2 == 0]
[0, 2, 4, 6, 8]
# for循环的结果只选择是2和3的倍数的
>>> [i for i in range(10) if i % 2 == 0 and i % 3 == 0]
[0, 6]
# for循环的结果只选择偶数,并且应用str函数
>>> [str(i) for i in range(10) if i % 2 == 0]
['0''2''4''6''8']

嵌套循环

假如我们展开一个二维矩阵,如下面的m,我们可以用嵌套循环实现。

m = [[1,2,3],
    [4,5,6],
    [7,8,9]
    ]

n = []
for row in m:
   for col in row:
       n.append(col)
print(n)

用列表推导,最外层的for循环得到的row,可以在内层中使用

m = [[1,2,3],
    [4,5,6],
    [7,8,9]
    ]
n = [col for row in m for col in row]
print(n)

再比如下面这个例子

>>> [a + b for a in '123' for b in 'abc']
['1a''1b''1c''2a''2b''2c''3a''3b''3c']

更多用法

列表推导的用法比较灵活,我们不一定要把所有的都掌握,但是要能看懂。

>>> dic = {"k1":"v1","k2":"v2"}
>>> a = [k+":"+v for k,v in dic.items()]
>>> a
['k1:v1''k2:v2']

1.2 集合推导

集合推导的语法与列表推导一样,只是它是用”{}“,而且,集合会自动去重

>>> { i for i in range(10)}
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> { 0 if i % 2 == 0 else 1 for i in range(10)}
{0, 1}

1.3 字典推导

字典推导的语法也与其他的类似,只不过在最前面的格式是key:value,而且也是会去重

>>> { i : i.upper() for i in 'hello world'}
{'h''H''e''E''l''L''o''O'' '' ''w''W''r''R''d''D'}
>>> { str(i) : i*i for i in range(10)}
{'0': 0, '1': 1, '2': 4, '3': 9, '4': 16, '5': 25, '6': 36, '7': 49, '8': 64, '9': 81}

1.4 元组推导?不存在的

既然用[]就能做列表推导,那用()是不是就能做元组推导了?不是的,因为()被用在了一种特殊的对象上:生成器(generator)。

>>> a = (i for i in range(10))
>>> print(a)

  
    at 0x000001A6100869C8> >>> 
   type(a) 
   
    'generator'> 
   
  

生成器是一个顺序产生元素的对象,只能顺序访问,只能前进,而且只能遍历一次。

可以使用next()函数取下一个元素,取不到就会报StopIteration异常,也可以使用for循环遍历。

生成式没法用下标访问,用next访问直到报异常

>>> a = (i for i in range(0,2))
>>> a[0]
Traceback (most recent call last):
 File "  " , line 1, in 
  
    TypeError: 
   'generator' object is not subscriptable >>> next(a) 0 >>> next(a) 1 >>> next(a) Traceback (most recent call last):  File 
   "  " , line 1, 
   in 
   
     StopIteration 
   
  

用for循环遍历

>>> a = (i for i in range(0,2))
>>> for i in a:
...   print(i)
...
0
1

先用next访问,再用for循环

>>> a = (i for i in range(0,3))
>>> next(a)
0
>>> for i in a:
...   print(i)
...
1
2

我们可以加上list,tuple,set等做强转,但是list和set就没必要了,如果想初始化成tuple,就用tuple做强转。强转的时候不需要再加多余的括号。

>>> a = tuple(i for i in range(0,3))
>>> a
(0, 1, 2)
>>> a = tuple( (i for i in range(0,3)) )
>>> a
(0, 1, 2)

生成式是惰式计算的,就是你确实用到这个元素了,它才去计算,好处就是节省了内存,但是坏处是不能随机访问。

2. 推导式的性能

2.1 列表推导式与循环的性能

我们用timeit模块去比较一下性能。

import timeit

def getlist1():  
   l = []  
   for i in range(10000):
       l.append(i)
   return l

def getlist2():
   return [i for i in range(10000)]

# 各执行10000次
t1 = timeit.timeit('getlist1()',"from __main__ import getlist1", number=10000)
t2 = timeit.timeit('getlist2()',"from __main__ import getlist2", number=10000)

print('循环方式:',t1)
print('推导式方式:',t2)

执行结果如下:

循环方式: 5.343517699991935
推导式方式: 2.6003115000057733

可见循环的方式比推导式慢了一倍,为什么会有这个问题呢?我们直接反编译看这两个的区别,用dis模块可以反编译Python代码,产生字节码。

源代码及行数如下

getlist1的反编译如下,左边红色对应源代码的行数,蓝色圈内就是第6行代码对应的字节码,我们可以看到,它有一个传参并且调用方法append的过程,调用函数的代价是比较大的。

再来看一下列表推断的反编译结果

首先从字节码数量上来比列表推断就比用循环调append要少的多,而且列表推断没有使用方法调用,直接用了这个指令LIST_APPEND,在Python官网上的解释是这样的。

实际上这个解释是有误导性,字节码中使用LIST_APPEND和在Python代码中调用append是完全不一样的,只不过这种底层的东西没有很多人关心,它们的功能是一样的。在2008年的时候就有人给Python代码提patch,希望能自动将list.append()进行优化,直接优化成LIST_APPEND而不是通过函数调用,但是目前还没被采纳。

提出者希望能在编译的时候加一些选项,比如像gcc可以使用-O1,-O2等进行不同级别的优化,但是目前CPython是没有这些选项的,因为大多数的Python开发者并不关心性能。

如果我们把上面的列表换成集合或者字典,差别会更大,所以能用推导式的地方尽量用推导式,可以提高性能。

2.2 列表推导式与生成器推导式的性能

其实这两个并不具备可比性,因为生成的结果并不是一个东西。我们可以很容易的预测,产生生成器的推导式性能要好于列表推导式,但是用的时候生成器就不如列表了。

import timeit

def getlist1():  
   return [i for i in range(10000)]

def getlist2():
   return (i for i in range(10000))

# 各执行10000次
t1 = timeit.timeit('getlist1()',"from __main__ import getlist1", number=10000)
t2 = timeit.timeit('getlist2()',"from __main__ import getlist2", number=10000)

print('列表:',t1)
print('生成器:',t2)

def getlist11():  
   a = [i for i in range(10000)]
   sum = 0
   for i in a:
       sum += i

def getlist22():
   a = (i for i in range(10000))
   sum = 0
   for i in a:
       sum += i

# 各执行10000次
t1 = timeit.timeit('getlist11()',"from __main__ import getlist11", number=10000)
t2 = timeit.timeit('getlist22()',"from __main__ import getlist22", number=10000)

print('列表:',t1)
print('生成器:',t2)

执行结果:

列表: 2.5977418000111356
生成器: 0.006076899997424334
列表: 6.336311199993361
生成器: 9.181903699995019

生成器产生的性能远大于列表,但是遍历的时候不如列表,但是总体上看好像生成器好。不过不要忘了,生成器不能随机访问,而且只能用一次。所以这两种对象,就是在合适的地方用合适的类型,不一定哪一种比哪一种更好。

文章来源网络,作者:管理,如若转载,请注明出处:https://shuyeidc.com/wp/220606.html<

(0)
管理的头像管理
上一篇2025-04-14 15:50
下一篇 2025-04-14 15:51

相关推荐

  • 骨干网络体系结构能干什么?骨干网络体系结构的作用

    骨干网络体系结构是现代信息社会的“超级高速公路网”,它通过分层设计、冗余备份和智能调度,确保海量数据在全球范围内高速、稳定、安全地传输,是支撑云计算、物联网及人工智能应用的底层基石,想象一下,如果你把互联网比作一个巨大的城市交通系统,那么骨干网络就是连接各个城市的主干道和立交桥,没有它,你的每一次微信发送、每一……

    2026-06-18
    0
  • 高io数据库可以干什么用?高io数据库适合什么场景

    高IO数据库的核心价值在于通过极高的读写吞吐量,解决海量数据场景下的性能瓶颈,是支撑高并发交易、实时分析及大规模内容分发的关键基础设施,在数字化转型的深水区,数据不再仅仅是静态的记录,而是流动的资产,传统的机械硬盘或普通SSD早已无法满足现代应用对速度的极致追求,高IO(Input/Output)数据库,就是那……

    2026-06-18
    0
  • 高io服务器性能如何?高io服务器适合什么场景

    高IO服务器并非单纯指代某种硬件,而是指在随机读写、高并发连接及小文件处理场景下,具备极致IOPS(每秒输入输出操作次数)和低延迟特性的计算资源,它是支撑现代高并发应用稳定运行的核心基石,在2026年的数字化浪潮中,业务负载早已从简单的静态页面展示演变为复杂的实时数据处理,许多开发者在排查系统瓶颈时,往往忽略了……

    2026-06-18
    0
  • 隔离网络空间哪里便宜?国内隔离网络空间价格

    隔离网络空间并没有统一的“便宜”标准,其成本高度取决于物理隔离等级、带宽需求及安全合规要求,通常物理网闸方案初期投入较高但长期运维成本低,而逻辑隔离方案虽初期便宜但存在潜在安全风险,建议根据业务敏感度选择混合隔离架构以平衡成本与安全,在数字化时代,企业构建独立网络环境的需求日益增长,但“隔离网络空间哪里便宜”这……

    2026-06-18
    0
  • 骨干网络体系结构设备为何故障?常见原因有哪些

    骨干网络体系结构设备故障的核心原因通常归结为硬件老化、配置错误、物理链路中断及外部攻击四大类,其中电源模块失效与光模块性能衰减是占比最高的隐性故障源,骨干网作为数字经济的“大动脉”,其稳定性直接关乎国计民生,当核心路由器或交换机出现丢包、震荡甚至宕机时,运维人员往往面临巨大的压力,很多人第一反应是检查软件配置……

    2026-06-18
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注