第七章类

类的定义

Python类以保留字 class 开始, 后面跟类名。

class PapayaWhip:  ①
    pass           ②
类名是 PapayaWhip, 没有从其他类继承。 类名通常是大写字母分隔, 如EachWordLikeThis, 但这只是个习惯,并非必须。
你可能猜到,类内部的内容都需缩进,就像函数中的代码一样, if 语句, for 循环, 或其他代码块。第一行非缩进代码表示到了类外。
         pass是python的保留关键字,一个类如果什么都不做需要写pass

类的初始化方法__init__()

class Fib:
    '''生成菲波拉稀数列的迭代器'''  ①

    def __init__(self, max):      ②
类同样可以 (而且应该) 具有docstring, 与模块(module)和方法一样。
类实例创建后,__init__() 方法被立即调用。很容易将其——但技术上来说不正确——称为该类的“构造函数” 。
 因为__init__() 方法调用时,对象已经创建了,你已经有了一个合法类对象的引用。
在  __init__()  方法中,  self  指向新创建的对象; 在其他类对象中, 它指向方法所属的实例。

尽管需在定义方法时显式指定self ,调用方法时并  必须明确指定。 Python 会自动添加。

实例化类

>>> import fibonacci2
>>> fib = fibonacci2.Fib(100)  ①
>>> fib                        ②
<fibonacci2.Fib object at 0x00DB8810>
>>> fib.__class__              ③
<class 'fibonacci2.Fib'>
>>> fib.__doc__                ④
'生成菲波拉稀数列的迭代器'

你正创建一个 Fib 类的实例(在fibonacci2 模块中定义) 将新创建的实例赋给变量fib
你传入一个参数 100, 这是Fib__init__()方法作为max参数传入的结束值。
fib 是 Fib 的实例。
每个类实例具有一个内建属性, __class__, 它是该对象的类。 
你可以用__doc__属性访问对象的 docstring ,就像函数或模块中的一样。 类的所有实例共享一份 docstring
Python里面 没有显式的  new  操作符。


实例变量

class Fib:
    def __init__(self, max):
        self.max = max        ①
self.max 是实例内 “全局” 的。 这意味着可以在其他方法中访问它。
实例变量直接在__init__方法内定义即可

实例变量特定于某个类的实例, 每个实例会记住自己的值。


斐波那契序列迭代器

class Fib:                                        ①
    def __init__(self, max):                      ②
        self.max = max

    def __iter__(self):                           ③
        self.a = 0
        self.b = 1
        return self

    def __next__(self):                           ④
        fib = self.a
        if fib > self.max:
            raise StopIteration                   ⑤
        self.a, self.b = self.b, self.a + self.b
        return fib                                ⑥

从无到有创建一个迭代器, fib 应是一个类,而不是一个函数。
“调用” Fib(max) 会创建该类一个真实的实例,并以max做为参数调用__init__() 方法。
 __init__() 方法以实例变量保存最大值,以便随后的其他方法可以引用。
当有人调用iter(fib)的时候,__iter__()就会被调用。(正如你等下会看到的, for 循环会自动调用它, 你也可以自己手动调用。) 在完成迭代器初始化后,(在本例中, 重置我们两个计数器 self.a 和 self.b), __iter__() 方法能返回任何实现了 __next__() 方法的对象。
在本例(甚至大多数例子)中, __iter__() 仅简单返回 self, 因为该类实现了自己的 __next__() 方法。
当有人在迭代器的实例中调用next()方法时,__next__() 会自动调用。 随后会有更多理解。
当 __next__() 方法抛出 StopIteration 异常, 这是给调用者表示迭代用完了的信号。
和大多数异常不同, 这不是错误;它是正常情况,仅表示迭代器没有值可产生了。
 如果调用者是 for 循环, 它会注意到该 StopIteration 异常并优雅的退出。 (换句话说,它会吞掉该异常。) 这点神奇之处就是使用 for 的关键。
为了分离出下一个值, 迭代器的 __next__() 方法简单 return该值。
不要使用 yield ; 该语法上的小甜头仅用于你使用生成器的时候。 这里你从无到有创建迭代器,使用 return 代替。

调用迭代器

>>> from fibonacci2 import Fib
>>> for n in Fib(1000):
...     print(n, end=' ')
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987

  • 如你所见,for 循环调用 Fib(1000)。 这返回Fib 类的实例。 叫它 fib_inst
  • 背地里,且十分聪明的, 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__()和__next__()方法

    >>> import plural6
    >>> r1 = plural6.LazyRules()
    >>> r2 = plural6.LazyRules()
    >>> r1.rules_filename                               ①
    'plural6-rules.txt'
    >>> r2.rules_filename
    'plural6-rules.txt'
    >>> r2.rules_filename = 'r2-override.txt'           ②
    >>> r2.rules_filename
    'r2-override.txt'
    >>> r1.rules_filename
    'plural6-rules.txt'
    >>> r2.__class__.rules_filename                     ③
    'plural6-rules.txt'
    >>> r2.__class__.rules_filename = 'papayawhip.txt'  ④
    >>> r1.rules_filename
    'papayawhip.txt'
    >>> r2.rules_filename                               ⑤
    'r2-overridetxt'

    类的每个实例继承了 rules_filename 属性及它在类中定义的值。
    修改一个实例属性的值不影响其他实例……
    ……也不会修改类的属性。可以使用特殊的 __class__ 属性来访问类属性(于此相对的是单独实例的属性)。
    如果修改类属性, 所有仍然继承该实例的值的实例 (如这里的r1 ) 会受影响。
    已经覆盖(overridden)了该属性(如这里的 r2 )的所有实例 将不受影响。























评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值