1. __getitem__实现可迭代对象。要将一个对象变成一个可迭代的对象,通常都要实现__iter__。但是如果没有__iter__的话,实现了__getitem__也可以实现迭代。我们还是用第一章扑克牌的例子来看下
  1. class FrenchDeck:
  2.     ranks=[str(n) for n in range(2,11)] + list('JQKA')
  3.     suits='spades diamonds clubs hearts'.split()
  4.     def __init__(self):
  5.         self._cards=[Card(rank,suit) for suit in self.suits for rank in self.ranks]
  6.     def __len__(self):
  7.         return len(self._cards)
  8.     def __getitem__(self, position):
  9.         return self._cards[position]
  10.  
  11. if __name__ == "__main__":
  12.     deck=FrenchDeck()
  13.     for d in deck:
  14.         print d
  1. E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter11.py
  1. Card(rank='2', suit='spades')
  1. Card(rank='3', suit='spades')
  1. Card(rank='4', suit='spades')
  1. Card(rank='5', suit='spades')
  1. Card(rank='6', suit='spades')
  1. Card(rank='7', suit='spades')
  1. Card(rank='8', suit='spades')
  1. Card(rank='9', suit='spades')
  1. Card(rank='10', suit='spades')
  1. Card(rank='J', suit='spades')
  1. Card(rank='Q', suit='spades')
  1. Card(rank='K', suit='spades')
  1. Card(rank='A', suit='spades')
  1. Card(rank='2', suit='diamonds')
  1. Card(rank='3', suit='diamonds')
  1. Card(rank='4', suit='diamonds')
  1. Card(rank='5', suit='diamonds')
  1. Card(rank='6', suit='diamonds')
  1. Card(rank='7', suit='diamonds')
  1. Card(rank='8', suit='diamonds')
  1. Card(rank='9', suit='diamonds')
  1. Card(rank='10', suit='diamonds')
  1. Card(rank='J', suit='diamonds')
  1. Card(rank='Q', suit='diamonds')
  1. Card(rank='K', suit='diamonds')
  1. Card(rank='A', suit='diamonds')
  1. Card(rank='2', suit='clubs')
  1. Card(rank='3', suit='clubs')
  1. Card(rank='4', suit='clubs')
  1. Card(rank='5', suit='clubs')
  1. Card(rank='6', suit='clubs')
  1. Card(rank='7', suit='clubs')
  1. Card(rank='8', suit='clubs')
  1. Card(rank='9', suit='clubs')
  1. Card(rank='10', suit='clubs')
  1. Card(rank='J', suit='clubs')
  1. Card(rank='Q', suit='clubs')
  1. Card(rank='K', suit='clubs')
  1. Card(rank='A', suit='clubs')
  1. Card(rank='2', suit='hearts')
  1. Card(rank='3', suit='hearts')
  1. Card(rank='4', suit='hearts')
  1. Card(rank='5', suit='hearts')
  1. Card(rank='6', suit='hearts')
  1. Card(rank='7', suit='hearts')
  1. Card(rank='8', suit='hearts')
  1. Card(rank='9', suit='hearts')
  1. Card(rank='10', suit='hearts')
  1. Card(rank='J', suit='hearts')
  1. Card(rank='Q', suit='hearts')
  1. Card(rank='K', suit='hearts')
  1. Card(rank='A', suit='hearts')

从输出结果可以看到,通过for d in deck迭代的方式也能遍历整个_card数组。迭代器环境会先尝试__iter__方法,在尝试__getitem__.也就是如果对象不支持迭代协议,就会尝试索引运算。迭代环境是通过调用内置函数iter去尝试__iter__方法来实现的,这种方法返回一个迭代器对象,如果提供Python就会重复调用这个迭代器对象的next方法,知道发生StopIteration异常,如果没找到这类__iter__方法,Python就会改用__getitem__机制,通过偏移量重复索引,直至发生IndexError异常

  1. 但是这个FrenchDeck有个问题:无法洗牌,从上面的结果来看,发票的顺序都是按照每个花色依次排列好的。那么如何洗牌了。这就需要用到随机数的方法了
  1. 我们用random.shuffle的方法来做随机数:
  1. list=[1,2,3]
  2. random.shuffle(list)
  3. print list
  1. 得到的结果是[3, 2, 1]
  1. Shuffle的实现代码如下:
  1. if random is None:
  2.     random = self.random
  3. _int = int
  4. for i in reversed(xrange(1, len(x))):
  5.     # pick an element in x[:i+1] with which to exchange x[i]
  6.     j = _int(random() * (i+1))
  7.     x[i], x[j] = x[j], x[i]
  1. 其实也比较简单,就是通过产生随机数来颠倒列表中的排列。那么是否可以根据这个函数来对扑克牌进行随机排列呢。我们来试下:
  1. deck=FrenchDeck()
  2. random.shuffle(deck)
  3. for d in deck:
  4.     print d
  1. E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter11.py
  1. Traceback (most recent call last):
  1.   File "E:/py_prj/fluent_python/chapter11.py", line 32, in <module>
  1.     random.shuffle(deck)
  1.   File "E:\python2.7.11\lib\random.py", line 291, in shuffle
  1.     x[i], x[j] = x[j], x[i]
  1. AttributeError: FrenchDeck instance has no attribute '__setitem__'
  1. 提示没有实现__setitem__ 为什么会错误呢。在Traceback里面已经写得很清楚了,因为:x[i], x[j] = x[j], x[i]。__getitem__是在deck[i]的时候调用,但是要赋值的时候比如deck[i]=value的时候得调用__setitem__
  1. 因此我们需要加上__setitem__
  1. def __setitem__(self, key, value):
  2.     self._cards[key]=value
  1. 再次运行得到结果如下:可以看到扑克牌完全是随机排列的了
  1. E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter11.py
  1. Card(rank='10', suit='diamonds')
  1. Card(rank='3', suit='hearts')
  1. Card(rank='4', suit='diamonds')
  1. Card(rank='A', suit='clubs')
  1. Card(rank='4', suit='spades')
  1. Card(rank='K', suit='clubs')
  1. Card(rank='8', suit='clubs')
  1. Card(rank='2', suit='clubs')
  1. Card(rank='8', suit='hearts')
  1. Card(rank='7', suit='diamonds')
  1. Card(rank='5', suit='hearts')
  1. Card(rank='10', suit='hearts')
  1. Card(rank='6', suit='hearts')
  1. Card(rank='Q', suit='clubs')
  1. Card(rank='J', suit='hearts')
  1. Card(rank='10', suit='spades')
  1. Card(rank='9', suit='spades')
  1. Card(rank='2', suit='diamonds')
  1. Card(rank='2', suit='spades')
  1. Card(rank='10', suit='clubs')
  1. Card(rank='3', suit='clubs')
  1. Card(rank='5', suit='spades')
  1. Card(rank='5', suit='clubs')
  1. Card(rank='Q', suit='hearts')
  1. Card(rank='3', suit='diamonds')
  1. Card(rank='J', suit='spades')
  1. Card(rank='7', suit='spades')
  1. Card(rank='8', suit='spades')
  1. Card(rank='6', suit='spades')
  1. Card(rank='Q', suit='diamonds')
  1. Card(rank='9', suit='diamonds')
  1. Card(rank='8', suit='diamonds')
  1. Card(rank='4', suit='clubs')
  1. Card(rank='6', suit='diamonds')
  1. Card(rank='9', suit='clubs')
  1. Card(rank='K', suit='spades')
  1. Card(rank='4', suit='hearts')
  1. Card(rank='J', suit='diamonds')
  1. Card(rank='5', suit='diamonds')
  1. Card(rank='6', suit='clubs')
  1. Card(rank='A', suit='spades')
  1. Card(rank='9', suit='hearts')
  1. Card(rank='K', suit='diamonds')
  1. Card(rank='7', suit='clubs')
  1. Card(rank='A', suit='hearts')
  1. Card(rank='Q', suit='spades')
  1. Card(rank='A', suit='diamonds')
  1. Card(rank='J', suit='clubs')
  1. Card(rank='3', suit='spades')
  1. Card(rank='2', suit='hearts')
  1. Card(rank='7', suit='hearts')
  1. Card(rank='K', suit='hearts')
  1. 抽象基类:
  1. 抽象基类的作用类似于JAVA中的接口。在接口中定义各种方法,然后继承接口的各种类进行具体方法的实现。抽象基类就是定义各种方法而不做具体实现的类,任何继承自抽象基类的类必须实现这些方法,否则无法实例化。
  1. 那么抽象基类这样实现的目的是什么呢? 假设我们在写一个关于动物的代码。涉及到的动物有鸟,狗,牛。首先鸟,狗,牛都是属于动物的。既然是动物那么肯定需要吃饭,发出声音。但是具体到鸟,狗,牛来说吃饭和声音肯定是不同的。
  1. 需要具体去实现鸟,狗,牛吃饭和声音的代码。概括一下抽象基类的作用:定义一些共同事物的规则和行为。
  1. 来看下具体的代码实现,定义一个抽象基类的简单方法如下: 首先在Dog,Bird,Cow都继承自Animal Animal中定义了eatvoice两个方法
  1. 任何从Animal中继承的子类都必须实现eatvoice方法。否则调用的时候会报错class Animal(object):

  1.     def eat(self):
  2.         raise NotImplementedError
  3.     def voice(self):
  4.         raise NotImplementedError
  5. class Dog(Animal):
  6.     def voice(self):
  7.         print 'wow....'
  8. class Bird(Animal):
  9.     def voice(self):
  10.         print 'jiji....'
  11. class Cow(Animal):
  12.     def voice(self):
  13.         print 'Oh.....'
  14. if __name__ == "__main__":
  15.     d=Dog()
  16.     d.voice()
  1. d.eat()
  1. 执行结果如下, voice可以正常执行,但是eat却报错了
  1. E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter11.py
  1. wow....
  1. Traceback (most recent call last):
  1.   File "E:/py_prj/fluent_python/chapter11.py", line 54, in <module>
  1.     d.eat()
  1.   File "E:/py_prj/fluent_python/chapter11.py", line 33, in eat
  1.     raise NotImplementedError
  1. NotImplementedError
  1. 这样实现有个缺点,就是只有子类调用eat方法的时候才会报错。子类是可以正常实例化的。但是你能够想象鸟,狗,牛不会吃饭么? 如果不会吃饭那肯定不算是动物了。所以正常的实现应该是如果没有实现eat方法,实例化就应该是失败的。那么这里就要用到抽象基类的一般使用方法.代码修改如下:
  1. Import abc
  1. class Animal(object):
  2.     __metaclass__ = abc.ABCMeta
  3.     @abc.abstractmethod
  4.     def eat(self):
  5.         return
  6.     @abc.abstractmethod
  7.     def voice(self):
  8.         return
  1. if __name__ == "__main__":
  2.     d=Dog()
  1. 结果如下,代码无法实例化,提示没有实现eat方法。这样就完美的达到了我们的目的。
  1. E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter11.py
  1. Traceback (most recent call last):
  1.   File "E:/py_prj/fluent_python/chapter11.py", line 56, in <module>
  1.     d=Dog()
  1. TypeError: Can't instantiate abstract class Dog with abstract methods eat
  1.  
  1. 完整代码修改如下class Animal(object):
  1.     __metaclass__ = abc.ABCMeta
  2.     @abc.abstractmethod
  3.     def eat(self):
  4.         return
  5.     @abc.abstractmethod
  6.     def voice(self):
  7.         return
  8. class Dog(Animal):
  9.     def voice(self):
  10.         print 'wow....'
  11.     def eat(self):
  12.         print 'Dog eat....'
  13. class Bird(Animal):
  14.     def voice(self):
  15.         print 'jiji....'
  16.     def eat(self):
  17.         print 'Bird eat....'
  18. class Cow(Animal):
  19.     def voice(self):
  20.         print 'Oh.....'
  21.     def eat(self):
  22.         print 'Cow eat....'
  23. if __name__ == "__main__":
  24.     d=Dog()
  25.     b=Bird()
  26.     c=Cow()
  27.     d.voice()
  28.     d.eat()
  29.     b.voice()
  30.     b.eat()
  31.     c.voice()
  32.     c.eat()
  1. E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter11.py
  1. wow....
  1. Dog eat....
  1. jiji....
  1. Bird eat....
  1. Oh.....
  1. Cow eat....
  1. 除了继承,还有一种注册的方法可以将类和抽象基类关联起来:Animal.register(Cat)
  1. class Cat(object):
  2.     def voice(self):
  3.         print 'miao.....'
  4.     def eat(self):
  5.         print 'Cat eat....'
  6. Animal.register(Cat)
  7.  
  8. if __name__ == "__main__":
  9.     c=Cat()
  10.     c.eat()
  11.     c.voice()
  1. E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter11.py
  1. Cat eat....
  1. miao...
  1. 继承和注册这两种方法有什么区别呢:区别在于通过继承能够看到继承抽象基类的所有类,而用注册的方法却看不到。
  1. for sc in Animal.__subclasses__():
  2.     print sc.__name__
  1. E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter11.py
  1. Dog
  1. Bird
  1. Cow
  1. 执行结果里面,只有Dog,Bird,Cow并没有Cat
  1. 最后介绍一种抽象子类的注册方式:__subclasshook__
  1. class Animal(object):
  2.     __metaclass__ = abc.ABCMeta
  3.     @abc.abstractmethod
  4.     def eat(self):
  5.         return
  6.     @abc.abstractmethod
  7.     def voice(self):
  8.         return
  9.     @classmethod
  10.     def __subclasshook__(cls, c):
  11.         if cls is Animal:  :⑴
  12.             if any("eat" in cat.__dict__ for cat in c.__mro__):⑵
  13.                 return True    
  14.         return NotImplementedError 
  1.  
  1. class Cat(object):
  2.     def voice(self):
  3.         print 'miao.....'
  4.     def eat(self):
  5.         print 'Cat eat....'
  1.  
  1. if __name__ == "__main__":
  2.     c=Cat()
  3.     print isinstance(Cat(),Animal)
  4.     print Animal.__subclasshook__(Cat)
  1. E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter11.py
  1. True
  1. True
  1. (1)首先判断cls是否属于Animal,在这里__subclasshook__classmethod修饰,证明是一个对象的方法,因此cls肯定等于Animal
  1. (2)首先得到c.__mro__. 当调用isinstance(Cat(),Animal)或者Animal.__subclasshook__(Cat)的时候,c就是Catc.__mro__就是得到Cat以及Cat的父类。c.__mro__=(<class '__main__.Cat'>, <type 'object'>)。  然后看下在Cat以及Cat的父类object的属性中是否有eat方法的实现,这里可以用eat或者voice方法来判断。如果是,则返回True
  1. (3)否则返回NotImplementedError

流畅python学习笔记:第十一章:抽象基类的更多相关文章

  1. python cookbook第三版学习笔记十六:抽象基类

    假设一个工程中有多个类,每个类都通过__init__来初始化参数.但是可能有很多高度重复且样式相同的__init__.为了减少代码.我们可以将初始化数据结构的步骤归纳到一个单独的__init__函数中 ...

  2. Python学习笔记(十一)

    Python学习笔记(十一): 生成器,迭代器回顾 模块 作业-计算器 1. 生成器,迭代器回顾 1. 列表生成式:[x for x in range(10)] 2. 生成器 (generator o ...

  3. 【python学习笔记】7.更加抽象

    [python学习笔记]7.更加抽象 类的定义就是执行代码块 在内存保存一个原始实例,可以通过类名来访问 类的实例化,是创建一个原始实例的副本, 并且所有成员变量与原始实例绑定 通过修改实例变量,可以 ...

  4. qml学习笔记(二):可视化元素基类Item详解(上半场anchors等等)

    原博主博客地址:http://blog.csdn.net/qq21497936本文章博客地址:http://blog.csdn.net/qq21497936/article/details/78516 ...

  5. python面对对象编程---------6:抽象基类

    抽象基本类的几大特点: 1:要定义但是并不完整的实现所有方法 2:基本的意思是作为父类 3:父类需要明确表示出那些方法的特征,这样在写子类时更加简单明白 用抽象基本类的地方: 1:用作父类 2:用作检 ...

  6. Python 接口:从协议到抽象基类

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px Helvetica } 抽象基类的常见用途:实现接口时作为超类使用.然后,说明抽象基类如何检查 ...

  7. #Python学习笔记:1-3章 (基于《python编程,从入门到实践)

    第1-3章 这个文档是记录我学习python时一些学习笔记以及一些想法也可以称作复习笔记 第一章:起步这一章主要是从第一个"hello world"程序到python环境的搭建与配 ...

  8. [Python学习笔记][第七章Python文件操作]

    2016/1/30学习内容 第七章 Python文件操作 文本文件 文本文件存储的是常规字符串,通常每行以换行符'\n'结尾. 二进制文件 二进制文件把对象内容以字节串(bytes)进行存储,无法用笔 ...

  9. [Python学习笔记][第五章Python函数设计与使用]

    2016/1/29学习内容 第四章 Python函数设计与使用 之前的几页忘记保存了 很伤心 变量作用域 -一个变量已在函数外定义,如果在函数内需要修改这个变量的值,并将这个赋值结果反映到函数之外,可 ...

随机推荐

  1. 抽象工厂(AbstractFactory)模式-创建型模式

    1.new 的问题 常见的对象创建方法: //创建一个Road对象 Road road=new Road(); new的问题:实现依赖,不能应对具体实例的变化 怎么理解上面这句话呢? 可以这样理解:我 ...

  2. nodejs中exports与module.exports的区别详细介绍

    如果模块是一个特定的类型就用Module.exports.如果模块是一个典型的"实例化对象"就用exports. exports.name = function() { conso ...

  3. 咦,好像可以自己做个webapi框架了-IRouteHandler的使用

    当我们学习到一定程度的时候,我们会想要去深入了解代码底层的东西,也更想拥有一个属于自己的框架,当然,博主也正是如此.本文可能成为编写一个webapi框架的开端.有研究MVC框架的朋友会发现,mvc框架 ...

  4. xssless - 自动化的XSS payload攻击器

    XSSLESS 一个用Python编写的自动化XSS 负载(payload)攻击器 用法: 记录请求 并结合Burp proxy 选择你想生成的请求,然后右键选择“保存项目” 使用xssless生成你 ...

  5. django 自定义过滤器(filter)处理较为复杂的变量的实例

    简述:django 在views中有数据需要通过字典(dict)的方式传递给template,该字典中又包含了字典,而且字典中的键值还是一个对象,在template中处理传递过来的数据的时候,字典不能 ...

  6. xtrabackup备份原理

    Percona XtraBackup工作原理 Percona XtraBackup是基于InnoDB的崩溃恢复功能.复制InnoDB数据文件,导致内部不一致的数据; 但随后它对文件执行崩溃恢复,使它们 ...

  7. [转] (CQRS)命令和查询责任分离架构模式(二) 之 Command的实现

    概述 继续引用上篇文章中的图片(来源于Udi Dahan博客),UI中的写入操作都将被封装为一个命令中,发送给Domain Model来处理. 我们遵循Domain Driven Design的设计思 ...

  8. PHP完成一个日历

    今天我们就用php中的数组合date 做一个日历. 先让我们回顾一下PHP里面的date . 1.时间戳:表示从计算机元年/UNIX纪年(0时区 1970/1/1 00:00:00)到当前事件的秒数. ...

  9. java web项目中 读取properties 路径的问题

    可以先获取项目的classPath String classPath = this.getClass().getResource("/").getPath();//获取classP ...

  10. org.apache.commons.lang3 的随机数生成

    apache org.apache.commons.lang3 的随机数生成工具,方便使用. String a12 = RandomStringUtils.random(4, "012345 ...