Week7-Lecture16-18
Lecture 16 Iterators¶
Reading 4.2 Implicit Sequences 隐式序列¶
核心思想:序列不一定要把每个元素都提前算好、存在内存里,可以"按需计算"(惰性计算,lazy computation)
4.2.1 Iterators¶
迭代器是一种"只能往前走一步一步取值"的对象,核心是两个内置函数:
iter(容器)→ 从一个容器(如列表)得到一个迭代器next(迭代器)→ 取下一个值,取完了就抛出StopIteration异常
- 迭代器有状态,记着自己走到哪了。对同一个容器调用两次
iter(),会得到两个互不影响、各自独立计数的迭代器。 - 但如果你把同一个迭代器赋给两个变量名(比如
u = t),这两个名字指向的是同一个迭代器,一个走了另一个也跟着走。 - 对一个迭代器再调用
iter(),会返回它自己(不是复制品)。这样设计是为了让你不用管手上的东西是"容器"还是"迭代器",统一用iter()包一层就行。
4.2.2 Iterable¶
能被iter()接受的东西就叫可迭代对象。即使是字典这种无序的集合,也可以被迭代。
4.2.3 Built-in Iterators¶
map、filter、zip、reversed 这些内置函数,在 Python 3 里返回的都是迭代器,而不是直接算出结果的列表。也就是说 map(f, s) 这一行代码本身不会去调用 f,只有你 next() 它或者用 list() 强制展开它的时候,f 才真正被逐个调用。这就是"惰性"在内置函数上的体现——调用和真正的计算是分离的。
4.2.4 For Statements¶
for x in 容器: 这个语法,本质上是 Python 帮你自动做了这几步:
- 对容器调用
__iter__()得到一个迭代器 - 反复调用这个迭代器的
__next__(),把结果赋给x - 直到捕获到
StopIteration就自动结束循环(这个异常被悄悄处理掉,不会报错)
| Python | |
|---|---|
4.2.5 Yield¶
自己动手写迭代器很麻烦,生成器用于用普通函数的写法定义一个迭代器,不用自己管理状态。
区别在于:普通函数用 return 交出结果并结束;生成器函数用 yield 交出一个值,但函数会"暂停"在这里,状态全部保留,等下次 __next__() 被调用时,从暂停的地方接着往下执行。
4.2.6 Iterable Interface¶
- 可迭代对象 = 数据本身的"容器",不会变。每次调用它的
__iter__()都能产出一个全新的迭代器。 - 迭代器 = 遍历过程中的"进度指针",会随着
next()不断变化。
4.2.7 Creating Iterables with Yield¶
4.2.8 Iterable Interface¶
反过来,如果你想手写一个不借助 yield 的迭代器类,核心只需要实现 __next__:每次调用返回下一个元素,并且在对象内部更新自身状态,状态耗尽时 raise StopIteration。
因为你可以完全自己控制什么时候算"结束",迭代器天然能表示无穷序列——只要 __next__ 永远不抛异常就行,比如一个不断 +1 吐出正整数的 Positives 类。这是隐式序列相比"提前存好的列表"最大的优势之一。
4.2.9 / 4.2.10 Stream¶
Stream(流)是另一种表示隐式序列的方式,可以理解成惰性版的链表:普通链表 Link(first, rest) 里 rest 是提前算好、直接存着的另一个链表;而 Stream 里的 rest 是一个还没算的函数,只有你真的去访问 .rest 属性时,才会调用这个函数把结果算出来,而且算出来后会缓存下来,不会重复计算第二次。
这带来一个和迭代器本质不同的特性:流可以被多次、反复地传给不同的纯函数处理,每次结果都一样,不会像迭代器那样"用一次就耗尽"。
Iterators, generators, streams共同解决一个无穷大的序列如何表示。
Lab 05 Iterators, Mutability¶
| Python | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | |
Lecture 17 Generators¶
Disc 06 Iterators, Generators¶
| Python | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | |
Lecture 18 Object¶
Reading 2.5 Object-Oriented Programming¶
OOP是一种组织程序的技术。汇聚了第二章中许多思想(抽象屏障、行为请求、局部状态)。
It enables a new metaphor for designing programs in which several independent agents interact within the computer.
对象是一种数据值,具有可通过点表示法访问的方法和属性。每个对象还都有一个类型,称为它的类。要创建新的数据类型,我们就实现新的类。
2.5.1 Objects and Classes¶
类是对象的模版,对象是类的实例。类定义了属性和方法。
| Python | |
|---|---|
2.5.2 Defining Classes¶
2.5.3 Message Passing and Dot Expressions¶
| Python | |
|---|---|
命名约定:类名用 CapWords(如 CheckingAccount),方法名用小写+下划线;以下划线开头的属性(如 _balance)表示"这是实现细节,外部不应直接访问"。
2.5.4 Class Attributes¶
- 直接改类属性(
Account.interest = 0.04)会影响所有没有同名实例属性的实例; - 给某个实例单独赋值(
kirk_account.interest = 0.08)只会创建一个新的实例属性,遮盖同名类属性,不会影响类或其他实例。
| Python | |
|---|---|
2.5.5 Inheritance¶
当两个类"很像,但一个是另一个的特殊情形"时,用继承避免重复代码。
- 术语:基类/父类(base/super class) vs 子类(subclass)
- 子类继承基类的全部属性,也可以**覆盖(override)**部分属性/方法
- 继承表达的是 is-a 关系("支票账户是一种账户"),而不是 has-a 关系
2.5.6 Using Inheritance¶
| Python | |
|---|---|
名字查找的递归规则(重要):在实例上找不到某名字时,Python 会依次到"实例所属的类 → 基类 → 基类的基类…"中查找。
接口(Interface):好的代码应该只依赖"对象有没有某个方法/属性",而不假设对象的具体类型。
2.5.7 Multiple Inheritance¶
| Python | |
|---|---|
多个基类都定义了同名属性/方法:从左到右、再往上的查找顺序
2.5.8 The role of object¶
- 对象系统的目的是让**关注点分离(separation of concerns)**更容易:每个对象封装自己的一部分状态,每个类实现程序逻辑的一部分。
- OOP 特别适合"多个独立个体相互交互"的系统建模(社交网络里的用户、游戏里的角色、物理模拟里的形状等)。
- 但不是所有逻辑都该塞进类里——如果用函数表达"输入到输出的映射关系"更自然,就该用函数。函数同样可以做到关注点分离。
- Python 是多范式语言,该用类还是用函数,是需要培养的设计判断力。
Homework 05¶
| Python | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | |