深入Python3 (Dive Into Python3)笔记7--类和迭代器

一切内容都是摘抄,主要是便于回忆和鼓励自己不要间断,更详细内容请见原帖地址:

《深入 python3 》中文版

http://woodpecker.org.cn/diveintopython3/index.html


7.1. 深入

7.2. 类的定义

PapayaWhip 类没有定义任何方法和属性, 但依据句法,应该在定义中有东西,这就是 pass 语句。 这是Python 保留字,意思是“继续,这里看不到任何东西”。 这是一个什么都不做的语句,是一个很好的占位符,如果你的函数和类什么都不想做(删空函数或类)。

Python中的pass 就像Java 或 C中的空大括号对 ({}) 。

尽管不是必须, Python 类 可以 具有类似构造函数的东西: __init__() 方法。

7.2.1. __init__() 方法

类实例创建后,__init__() 方法被立即调用。很容易将其——但技术上来说不正确——称为该类的“构造函数” 。 很容易,因为它看起来很像 C++ 的构造函数(按约定,__init__() 是类中第一个被定义的方法),行为一致(是类的新实例中第一片被执行的代码), 看起来完全一样。 错了, 因为__init__() 方法调用时,对象已经创建了,你已经有了一个合法类对象的引用。

每个方法的第一个参数,包括 __init__() 方法,永远指向当前的类对象。 习惯上,该参数叫 self。 该参数和C++或Java中 this 角色一样, 但 self 不是 Python的保留字, 仅仅是个命名习惯。 虽然如此,请不要取别的名字,只用 self; 这是一个很强的命名习惯。

在 __init__() 方法中, self 指向新创建的对象; 在其他类对象中, 它指向方法所属的实例。尽管需在定义方法时显式指定self ,调用方法时并 不 必须明确指定。 Python 会自动添加。

7.3. 实例化类

每个类实例具有一个内建属性, __class__, 它是该对象的类。 Java 程序员可能熟悉 Class 类, 包含方法如 getName() 和 getSuperclass() 获取对象相关元数据。 Python里面, 这类元数据由属性提供,但思想一致。

7.4. 实例变量

self.max是什么? 它就是实例变量。 与作为参数传入 __init__() 方法的 max完全是两回事。 self.max 是实例内 “全局” 的。 这意味着可以在其他方法中访问它。

7.5. 菲波拉稀迭代器

这些类的所有三种方法, __init__, __iter__, 和 __next__, 起始和结束均为一对下划线(_) 字符。 为什么这样? 并无什么神奇之处, 只是通常表示这是“特殊方法。” 唯一“特殊”的地方,就是这些方法不是直接调用的; 当你使用类或实例的某些语法时,Python会自动调用他们。

当有人调用iter(fib)的时候,__iter__()就会被调用。(正如你等下会看到的, for 循环会自动调用它, 你也可以自己手动调用。) 在完成迭代器初始化后,(在本例中, 重置我们两个计数器 self.a 和 self.b), __iter__() 方法能返回任何实现了 __next__() 方法的对象。 在本例(甚至大多数例子)中, __iter__() 仅简单返回 self, 因为该类实现了自己的 __next__() 方法。

当 __next__() 方法抛出 StopIteration 异常, 这是给调用者表示迭代用完了的信号。 和大多数异常不同, 这不是错误;它是正常情况,仅表示迭代器没有值可产生了。 如果调用者是 for 循环, 它会注意到该 StopIteration 异常并优雅的退出。 (换句话说,它会吞掉该异常。) 这点神奇之处就是使用 for 的关键。

为了分离出下一个值, 迭代器的 __next__() 方法简单 return该值。 不要使用 yield ; 该语法上的小甜头仅用于你使用生成器的时候。 这里你从无到有创建迭代器,使用 return 代替。

背地里,且十分聪明的, for 循环调用 iter(fib_inst), 它返回迭代器。 叫它 fib_iter。 本例中, fib_iter == fib_inst, 因为 __iter__() 方法返回 self,但for 循环不知道(也不关心)那些。

为“循环通过”迭代器, for 循环调用 next(fib_iter), 它又调用 fib_iter对象的 __next__() 方法,产生下一个菲波拉稀计算并返回值。 for 拿到该值并赋给 n, 然后执行n值的 for 循环体。

for循环如何知道什么时候结束?很高兴你问到。 当next(fib_iter) 抛出 StopIteration 异常时, for循环将吞下该异常并优雅退出。 (其他异常将传过并如常抛出。) 在哪里你见过 StopIteration 异常? 当然在 __next__() 方法。

iter(f) 调用 f.__iter__
next(f) 调用 f.__next__

7.6. 复数规则迭代器

我们继续之前,让我们近观 rules_filename。它没在 __iter__() 方法中定义。事实上,它没在任何方法中定义。它定义于类级别。它是 类变量, 尽管访问时和实例变量一样 (self.rules_filename), LazyRules 类的所有实例共享该变量。

  • 如果修改类属性, 所有仍然继承该实例的值的实例 (如这里的r1 ) 会受影响。
  • 已经覆盖(overridden)了该属性(如这里的 r2 )的所有实例 将不受影响。
  • 这里有点高级文件操作的技巧。 readline() 方法 (注意:是单数,不是复数 readlines()) 从一个打开的文件中精确读取一行,即下一行。(文件对象同样也是迭代器! 它自始至终是迭代器……)这里有点高级文件操作的技巧。 readline() 方法 (注意:是单数,不是复数 readlines()) 从一个打开的文件中精确读取一行,即下一行。(文件对象同样也是迭代器! 它自始至终是迭代器……)
  • 如果有一行 readline() 可以读, line 就不会是空字符串。 甚至文件包含一个空行, line 将会是一个字符的字符串 '/n' (回车换行符)。 如果 line 是真的空字符串, 就意味着文件已经没有行可读了。
  • 当我们到达文件尾时, 我们应关闭文件并抛出神奇的 StopIteration 异常。 记住,开门见山的说是因为我们需要为下一条规则找到一个匹配和应用功能。下一条规则从文件的下一行获取…… 但已经没有下一行了! 所以,我们没有规则返回。 迭代器结束。 ( 派对结束 )
  1. self.cache 将是一个我们匹配并应用单独规则的功能列表。 (至少那个 应该看起来熟悉!) self.cache_index 记录我们下一步返回的缓存条目。 如果我们还没有耗尽缓存 (举例 如果 self.cache 的长度大于 self.cache_index),那么我们就会命中一条缓存! 哇! 我们可以从缓存中返回匹配和应用功能而不是从无到有创建。
  2. 另一方面,如果我们没有从缓存中命中条目, 并且 文件对象也已关闭(这会发生, 在本方法下面一点, 正如你从预览的代码片段中所看到的),那么我们什么都不能做。 如果文件被关闭,意味着我们已经用完了它——我们已经从头至尾读取了模式文件的每一行,而且已经对每个模式创建并缓存了匹配和应用功能。文件已经读完;缓存已经用完;我也快完了。等等,什么?坚持一下,我们几乎完成了。

这真的是极乐世界? 嗯,是或不是。 这里有一些LazyRules 示例需要细想的地方: 模式文件被打开(在 __init__()中),并持续打开直到读取最后一个规则。 当Python退出或最后一个LazyRules 类的实例销毁,Python 会最终关闭文件,但是那仍然可能会是一个很长的时间。如果该类是一个“长时间运行”的Python进程的一部分,Python可能从不退出, LazyRules 对象就可能一直不会释放。

这种情况有解决办法。 不要在 __init__() 中打开文件并让其在一行一行读取规则时一直打开,你可以打开文件,读取所有规则,并立即关闭文件。或你可以打开文件,读取一条规则,用tell() 方法保存文件位置,关闭文件,后面再次打开它,使用seek() 方法 继续从你离开的地方读取。 或者你不需担心这些就让文件打开,如同本示例所做。 编程即是设计, 而设计牵扯到所有的权衡和限制。让一个文件一直打开太长时间可能是问题;让你代码太复杂也可能是问题。哪一个是更大的问题,依赖于你的开发团队,你的应用,和你的运行环境。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值