- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55582 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17626
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 447
- 主题
- 326
- 精华
- 1
- 分享
- 0
- 好友
- 79
TA的每日心情 | 慵懒 2020-7-12 09:52 |
|---|
签到天数: 116 天 [LV.6]常住居民II 管理员
 群组: 2018教师培训(呼和浩 群组: 2017-05-04 量化投资实 群组: 2017“草原杯”夏令营 群组: 2018美赛冲刺培训 群组: 2017 田老师国赛冲刺课 |
一、可迭代对象" } j( {/ i8 Y- I
字面意思:
, ?2 r" Q, ]" f对象:Python 中一切皆为对象(巧了 Java 也是(手动滑稽))+ G0 |' _' V! j8 v. F: e' _! G1 c4 t
可迭代:可更新迭代,重复、循环的一个过程,每次更新迭代都会获得新的内容
# c* H, P8 d+ S, w8 G专业角度:内部含有 '__iter__‘ 方法的对象
1 d& X) W3 M& d. A, m目前学过的可迭代对象:str、list、tuple、dict、set、range、文件句柄等
4 n4 a8 D/ J5 G9 Q: Z判断一个对象是否是可迭代对象:看是否有 '__iter__' 方法,dir() 可以获取一个对象的所有方法;或者使用 isinstance(object, collections.iterable) 来判断对象是否是可迭代对象的一个实例9 }! f) X' n1 Q" p
6 q6 M$ g6 n. o* a8 [
1 Q- A; [# D [# J p5 ]9 E4 b
优点:" i$ ^8 f3 d, S5 {# ]( y$ @
存储的数据直接能显示,比较直观:比如直接 print 一个可迭代对象,就会调用 __str__ 方法(相当于 Java 中的 toString),把可迭代对象的值打印出来
, a6 Y3 w2 j# O( {' e0 m拥有较多的方法,操作方便:增删查改等4 X6 q1 b [" d, L" w( t6 M
缺点:
E' \3 ~! o. O. T( J. [占用内存:一旦创建了一个可迭代对象,就会将该对象的内容全部加载到内存中4 V0 G, t! s1 K6 ^
不能直接通过 for 循环,不能直接取值(通过索引、key等)。诸如通过 for i in iterable 这种形式获取元素实际上也是调用了 __iter__ 方法先将可迭代对象转换成迭代器再进行获取5 A) E% Z' F! g' n
二、迭代器. N5 B, Q( K* D) \) _! P5 X
字面意思:器,工具,迭代器也就是可以一直更新迭代取值的工具; J6 s, K! [( D; W1 V9 r- p3 G$ I6 ~
专业角度:内部含有 __iter__ 方法且含有 __next__ 方法的对象就是迭代器;或者使用 isinstance(object, collections.iterator) 来判断对象是否是可迭代对象的一个实例9 x6 x3 |/ [& E- W t
把一个可迭代对象转换成迭代器:使用 iter() 方法或使用对象的 __iter__ 方法
9 C0 V% }5 m( ?3 F" U![]()
2 _! L' A" X, l# L迭代器取值:使用 next() 方法或对象的 __next__ 方法;当迭代器的值去玩了继续取,就会报StopIteration异常,所以一般使用迭代器需要做异常处理' d" W9 ]0 e! ^5 T+ g$ f7 i
3 M1 d; W1 j) E8 p- a. _+ g3 x
优点
4 j( v2 Z8 a9 N+ ~- _- U节省内存:迭代器并不会一次性将对象的值全部加载到内存中,而是需要时才加载(类似 sed)
6 V8 A9 I' O' [6 _$ ^! g1 q0 h7 i惰性机制:next 一次只取一个值,绝对不多取+ |8 R) x9 [/ I8 A/ @6 R2 X% C
缺点:% R7 w' b( S j8 W
速度慢:需要一直 next
2 u* I V; ^2 {1 Q% V3 T9 e/ B不能回头:只能一直往下取值,取过的值没保存就没了`
. L0 Q. _. {6 o' U3 R$ G3 ~/ k不能直观的看到里面的数据
N# p e* `9 E/ _0 v7 ]" E- v5 B三、可迭代对象与迭代器对比
! [2 }6 f! ~ C' ]; b$ @& X可迭代对象:. {* P6 \ A0 i* @5 ?0 c+ ~: m# ~
私有方法多,操作灵活(比如列表,字典的增删改查,字符串的常用操作方法等); I5 F& U6 {, |+ S3 C
直观,可以直接看到里面的数据) j' b$ P c" Z, \4 u3 T, T/ @
占用内存1 C- z# E' o. {, t+ U1 m
不能直接通过循环迭代取值0 u( M& L9 ^! Z [+ d( ^% c# R0 q. A
应用:当你侧重于对于数据可以灵活处理,并且内存空间足够,将数据集设置为可迭代对象是明确的选择
7 y2 M& K! [! p# k( B迭代器:7 G$ B0 Z; Z% B0 D, Z- c- U8 `, N2 l
节省内存,按需取值
! l6 }* g# E' y可以直接通过循环迭代取值
0 _/ s# h3 g# f, r; k数据不直观,操作方法单一% D" Q' p- c6 }, U3 ^0 B
应用:当你的数据量过大,大到足以撑爆你的内存或者你以节省内存为首选因素时,将数据集设置为迭代器是一个不错的选择2 U& E+ o8 P9 w- X3 M9 F( G2 K5 _
四、生成器
/ F4 G4 D8 J k! p3 o生成器的本质就是迭代器,唯一的区别是生成器是我们自己用代码构建的数据结构,迭代器是 Python 提供的,或者通过可迭代对象转化得来的
- K4 D; [6 ~$ {. G" I) B. p
& W ~0 U& r* `( t定义生成器的方式:8 Q! l" h8 i1 ~- \
通过生成器函数构建生成器
/ }4 _7 c2 P$ {
/ R+ g" [6 v2 |" g9 W; B, _' m; Q) v6 k) b
. s0 G& U: I7 E8 i# s
这就是最简单的生成器函数。实际上这个 yield 就替代了 return,不仅将函数变成了生成器函数,还会将后面的值在调用 __next__ 的时候返回出来; @8 i/ q, {+ `' A) w5 `3 \7 \
* l2 R# T* _8 T- E6 M2 J
也可以在一个函数里定义多个 yield: g! Y% |0 n* \ j; q
& D- e1 F2 f$ m1 t
之前说过,生成器本质上还是迭代器,一个 yield 对应一个 next,当 next 的数量超过了 yield,就会报 StopIteration' X4 c; x l1 z. ^
& f1 O* L) u( [* ?- hyield 与 return 的区别
: E% [ t& I. O' R( y1 Z! J9 u9 {+ q" `. u9 @4 c- W
return一般在函数中只设置一个,他的作用是终止函数,并且给函数的执行者返回值7 ]$ G* A4 {: d N2 ]$ C5 @9 M# D r
yield在生成器函数中可设置多个,他并不会终止函数,next会获取对应yield生成的元素. q1 w, j6 P5 i e/ T, X7 L
应用举例:7 `% {) E6 e) @ T2 H6 K" e
+ w. P4 v& k! C' k
买 5000 个包子,假设这个老板很厉害,一下子就把 5000 个包子做出来卖给我们,可是我们只有 5 个人,一下子吃不完,那包子就会冷掉、臭掉、被丢掉浪费了& q- i. o7 j6 U3 ~2 O- y' E+ c" U" }
+ s8 ]7 K9 V1 u
& h' V3 d0 L" D" o- B6 U" T" x如果这个老板可以在我们需要多少个包子就做出来多少个包子的话,这样做出来的包子就不会被浪费了(比如我们每个人一口气能吃 40 个包子,那每次就做 200 个包子):1 R8 _ d& {0 N* _9 i9 L! Z
+ X) t: p, u7 B6 h) w2 `
( }# s7 L2 _+ p除了 使用 next() 触发 yield 之外,生成器还有一种方法 send(),这个方法可以在调用 yield 的同时传值给生成器内部
' w, H& `' ]8 f) z: r/ k1 @9 R![]()
8 @8 l& G% U4 b% E0 Y可以看到在使用 next() 的时候,只能获取到 yield 的值,但不能传递值, L# y9 E" @$ y
. x& i( ]0 j! d& v/ M" z q1 E+ A
在使用 send() 的时候,可以将参数传入生成器中使用
1 {! `- G+ o, f- T7 A! J$ I q7 t) ~/ v/ K
需要注意的是第一次不能直接调用 send() 传参,因为每次调用生成器的时候,实际上只会返回 yield 后面的内容,然后生成器就停止了(睡眠了?),而 send() 传入的参数要通过 yield 传入生成器中(每次调用生成器在 yield 停止,然后在 yield 恢复继续允许),第一次调用并没有 yield 给我们传入参数,可以使用 send(None),可以打断点自己分析一下' N Q8 j1 c% ~- J1 N8 D2 j9 q2 H
* H3 v0 K' N P+ I$ {8 _+ r
yield 会将它后面跟着的对象直接返回,如果它后面跟着的是可迭代对象,也可以使用 yield from 将这个可迭代对象变成迭代器返回% v& _1 Y7 M( N& O1 ~6 K+ f7 v0 O6 ~
S7 ?" `9 Q& Q9 P5 E2 L
- s o. ^3 j+ ?. V% e/ h* }
yield from 是将列表中的每一个元素返回,所以写两个 yield from 并不会有交替执行的效果* w; L$ h2 f) }9 r" x9 m
+ z! l( P. O( x2 D6 v
" V$ s- f6 @, q' r+ ~6 F- q" Y# [
$ `$ x) z- I/ S! y
通过推导式构建生成器 ~/ s# O4 k; b, [ `( Q
列表推导式:
: J% V7 N' ]- [# l* @6 X4 ~ 3 T, ?& p- M* n7 W% _
生成器表达式:和列表推导式差不多,把 [] 改成 () 即可
7 Z( L6 V7 W: e: j9 m. H% E % j: B- X. c7 N' g
: D8 Z! a6 s3 U: v% z) B; v) E
列表推导式和生成器推导式的区别:
' ^/ ]# Z/ Z+ ^. C: a! U8 E5 l- W6 ~, J5 x$ I X& N
列表推导式比较耗内存,所有数据一次性加载到内存;而生成器表达式遵循迭代器协议,逐个产生元素
/ o) v# e2 g3 K s7 p得到的值不一样:列表推导式得到的是一个列表;生成器表达式获取的是一个生成器
0 \& t5 y" l# S1 g& H E. G2 s8 G列表推导式一目了然,生成器表达式只是一个内存地址
7 T* D( v3 y- @3 j/ C6 y0 b+ r, d" a/ I————————————————( J% X# H, H5 k' X1 P" {: R
版权声明:本文为CSDN博主「阿玮d博客」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
- h, `7 q' [' ? ^; m原文链接:https://blog.csdn.net/weixin_42511320/article/details/105676143* f# v Q+ t6 Z' q! p8 e* Y$ H8 \
- K# [4 @5 r! {2 N# h
: Z6 y, L/ J, j6 q, G: i; ?
|
zan
|