網站首頁 編程語言 正文
概述
Python中的列表推倒式(List Comprehension) 和 生成器表達式(Generator Expression)是兩種很相似的表達式,但含義卻不大不同,這里做一個對比。
列表推導式
列表推導式是比較常用的技術,能將本來需要for loop 和 if else 語句的情況簡化成一條指令,最終得到一個列表對象:
even = [e for e in range(10) if e % 2 == 0]
具體細節不過多展開,相信很多使用Python的人都已經足夠了解這種語法了。
需要注意的一點是,列表推導式不是惰性計算 ( Lazy Loading) 的,因此所有的列表成員都在聲明完語句后立即計算 (Eager Loading),因此在數組成員很多的情況下,速度會很慢,例如下面的在IPython環境里面的三個列表推導式的耗時統計:
In [1]: %timeit even = [e for e in range(100000) if e % 2 == 0]
5.5 ms ± 24.8 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [2]: %timeit even = [e for e in range(1000000) if e % 2 == 0]
58.9 ms ± 440 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [3]: %timeit even = [e for e in range(100000000) if e % 2 == 0]
5.65 s ± 26.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
可以看到隨著元素個數的增加,列表推導式執行的時間也相應變長,占用的內存也會變大。
有一種情況是,我們定義了很多很多的數組元素,但是最后并不是所有的元素都能用到,例如經過幾條命令,最后可能只有列表里面的前10個元素會用到,或者只有符合某些條件的元素會用到,這樣的話,Eager模式就白白花費了時間,白白花費了內存來創建很多用不到的元素,這顯然有很大的改進空間。
生成器表達式
生成器能表達式解決上面的問題,它的元素迭代是惰性的,因此只有需要的時候才生產出來,避免了額外的內存開銷和時間開銷: 生成器表達式不管元素數目多大,創建時都是常數時間,因為它并沒有立即創建元素。
那么生成器表達式的語法是怎么樣的呢,很簡單,只需要把列表推導式中的方括號改為圓括號:
even_gen = (e for e in range(10) if e % 2 == 0)
注意它的類型是生成器類型:
type(even_gen)
# generator
創建生成器表達式的耗時統計:
In [1]: %timeit even_gen = (e for e in range(100000) if e % 2 == 0)
376 ns ± 2.61 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
In [2]: %timeit even_gen = (e for e in range(10000000) if e % 2 == 0)
382 ns ± 1.63 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
In [3]: %timeit even_gen = (e for e in range(1000000000) if e % 2 == 0)
384 ns ± 2.85 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
可以看到隨著元素的增加,創建時間基本不變,而且比列表推導式的耗時要低不少。
使用場景選擇
那么是不是就是說使用中可以用生成器表達式替代列表推導式了呢,也不盡然,因為列表推導式得到的是一個列表,很多便捷操作(如slice等)可以作用到上面,而生成器表達式則不行:
In [17]: even = [e for e in range(10) if e % 2 == 0]
In [18]: even[:3]
Out[18]: [0, 2, 4]
In [19]: even_gen = (e for e in range(10) if e % 2 == 0)
In [20]: even_gen[:3]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Input In [20], in <cell line: 1>()
----> 1 even_gen[:3]
TypeError: 'generator' object is not subscriptable
而且兩者有一個致命的區別:生成器表達式只能迭代一次,而列表推導式可以使用很多次,舉例如下:
In [22]: even_gen = (e for e in range(10) if e % 2 == 0)
In [23]: for e in even_gen:
...: print(e)
...:
0
2
4
6
8
In [24]: for e in even_gen:
...: print(e)
...:
可以看到生成器表達式在第二次迭代的時候,里面已經沒有元素了!即第一次迭代已經全部生成出來了,而列表推導式是每次迭代都是有相同的內容:
In [25]: even = [e for e in range(10) if e % 2 == 0]
In [26]: for e in even:
...: print(e)
...:
0
2
4
6
8
In [27]: for e in even:
...: print(e)
...:
0
2
4
6
8
因此總結來說,使用建議如下:
- 如果要多次迭代時,建議使用列表推導式
- 如果數組很大或者有無窮個元素,建議使用生成器表達式
- 其他場景:兩者均可,自己看情況使用一個,如果沒有速度和方便度的問題即可,如果有問題換另一個再試試
參考
- stackoverflow.com/questions/4…
- docs.python.org/3/howto/fun…
總結
原文鏈接:https://juejin.cn/post/7185399576403116093
相關推薦
- 2022-06-13 安裝Docker配置阿里云鏡像加速(圖文教程)_docker
- 2022-03-24 sublime?text3解決Gosublime無法自動補全代碼的問題_Golang
- 2022-11-05 替代pod?update速度慢的lg_pod_plugin安裝使用詳解_IOS
- 2022-08-31 C語言數據的存儲專項分析_C 語言
- 2024-01-10 Maven導入org.apache.commons.lang3.StringUtils
- 2022-02-02 appname is automatically signed for development,
- 2023-03-20 解讀C#中ReadString的一些小疑惑_C#教程
- 2022-09-25 Shiro和SpringSecurity
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支