流畅python学习笔记:第十一章:抽象基类
- __getitem__实现可迭代对象。要将一个对象变成一个可迭代的对象,通常都要实现__iter__。但是如果没有__iter__的话,实现了__getitem__也可以实现迭代。我们还是用第一章扑克牌的例子来看下
- class FrenchDeck:
- ranks=[str(n) for n in range(2,11)] + list('JQKA')
- suits='spades diamonds clubs hearts'.split()
- def __init__(self):
- self._cards=[Card(rank,suit) for suit in self.suits for rank in self.ranks]
- def __len__(self):
- return len(self._cards)
- def __getitem__(self, position):
- return self._cards[position]
- if __name__ == "__main__":
- deck=FrenchDeck()
- for d in deck:
- print d
- E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter11.py
- Card(rank='2', suit='spades')
- Card(rank='3', suit='spades')
- Card(rank='4', suit='spades')
- Card(rank='5', suit='spades')
- Card(rank='6', suit='spades')
- Card(rank='7', suit='spades')
- Card(rank='8', suit='spades')
- Card(rank='9', suit='spades')
- Card(rank='10', suit='spades')
- Card(rank='J', suit='spades')
- Card(rank='Q', suit='spades')
- Card(rank='K', suit='spades')
- Card(rank='A', suit='spades')
- Card(rank='2', suit='diamonds')
- Card(rank='3', suit='diamonds')
- Card(rank='4', suit='diamonds')
- Card(rank='5', suit='diamonds')
- Card(rank='6', suit='diamonds')
- Card(rank='7', suit='diamonds')
- Card(rank='8', suit='diamonds')
- Card(rank='9', suit='diamonds')
- Card(rank='10', suit='diamonds')
- Card(rank='J', suit='diamonds')
- Card(rank='Q', suit='diamonds')
- Card(rank='K', suit='diamonds')
- Card(rank='A', suit='diamonds')
- Card(rank='2', suit='clubs')
- Card(rank='3', suit='clubs')
- Card(rank='4', suit='clubs')
- Card(rank='5', suit='clubs')
- Card(rank='6', suit='clubs')
- Card(rank='7', suit='clubs')
- Card(rank='8', suit='clubs')
- Card(rank='9', suit='clubs')
- Card(rank='10', suit='clubs')
- Card(rank='J', suit='clubs')
- Card(rank='Q', suit='clubs')
- Card(rank='K', suit='clubs')
- Card(rank='A', suit='clubs')
- Card(rank='2', suit='hearts')
- Card(rank='3', suit='hearts')
- Card(rank='4', suit='hearts')
- Card(rank='5', suit='hearts')
- Card(rank='6', suit='hearts')
- Card(rank='7', suit='hearts')
- Card(rank='8', suit='hearts')
- Card(rank='9', suit='hearts')
- Card(rank='10', suit='hearts')
- Card(rank='J', suit='hearts')
- Card(rank='Q', suit='hearts')
- Card(rank='K', suit='hearts')
- Card(rank='A', suit='hearts')
从输出结果可以看到,通过for d in deck迭代的方式也能遍历整个_card数组。迭代器环境会先尝试__iter__方法,在尝试__getitem__.也就是如果对象不支持迭代协议,就会尝试索引运算。迭代环境是通过调用内置函数iter去尝试__iter__方法来实现的,这种方法返回一个迭代器对象,如果提供Python就会重复调用这个迭代器对象的next方法,知道发生StopIteration异常,如果没找到这类__iter__方法,Python就会改用__getitem__机制,通过偏移量重复索引,直至发生IndexError异常
- 但是这个FrenchDeck有个问题:无法洗牌,从上面的结果来看,发票的顺序都是按照每个花色依次排列好的。那么如何洗牌了。这就需要用到随机数的方法了
- 我们用random.shuffle的方法来做随机数:
- list=[1,2,3]
- random.shuffle(list)
- print list
- 得到的结果是[3, 2, 1]
- Shuffle的实现代码如下:
- if random is None:
- random = self.random
- _int = int
- for i in reversed(xrange(1, len(x))):
- # pick an element in x[:i+1] with which to exchange x[i]
- j = _int(random() * (i+1))
- x[i], x[j] = x[j], x[i]
- 其实也比较简单,就是通过产生随机数来颠倒列表中的排列。那么是否可以根据这个函数来对扑克牌进行随机排列呢。我们来试下:
- deck=FrenchDeck()
- random.shuffle(deck)
- for d in deck:
- print d
- E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter11.py
- Traceback (most recent call last):
- File "E:/py_prj/fluent_python/chapter11.py", line 32, in <module>
- random.shuffle(deck)
- File "E:\python2.7.11\lib\random.py", line 291, in shuffle
- x[i], x[j] = x[j], x[i]
- AttributeError: FrenchDeck instance has no attribute '__setitem__'
- 提示没有实现__setitem__, 为什么会错误呢。在Traceback里面已经写得很清楚了,因为:x[i], x[j] = x[j], x[i]。__getitem__是在deck[i]的时候调用,但是要赋值的时候比如deck[i]=value的时候得调用__setitem__
- 因此我们需要加上__setitem__:
- def __setitem__(self, key, value):
- self._cards[key]=value
- 再次运行得到结果如下:可以看到扑克牌完全是随机排列的了
- E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter11.py
- Card(rank='10', suit='diamonds')
- Card(rank='3', suit='hearts')
- Card(rank='4', suit='diamonds')
- Card(rank='A', suit='clubs')
- Card(rank='4', suit='spades')
- Card(rank='K', suit='clubs')
- Card(rank='8', suit='clubs')
- Card(rank='2', suit='clubs')
- Card(rank='8', suit='hearts')
- Card(rank='7', suit='diamonds')
- Card(rank='5', suit='hearts')
- Card(rank='10', suit='hearts')
- Card(rank='6', suit='hearts')
- Card(rank='Q', suit='clubs')
- Card(rank='J', suit='hearts')
- Card(rank='10', suit='spades')
- Card(rank='9', suit='spades')
- Card(rank='2', suit='diamonds')
- Card(rank='2', suit='spades')
- Card(rank='10', suit='clubs')
- Card(rank='3', suit='clubs')
- Card(rank='5', suit='spades')
- Card(rank='5', suit='clubs')
- Card(rank='Q', suit='hearts')
- Card(rank='3', suit='diamonds')
- Card(rank='J', suit='spades')
- Card(rank='7', suit='spades')
- Card(rank='8', suit='spades')
- Card(rank='6', suit='spades')
- Card(rank='Q', suit='diamonds')
- Card(rank='9', suit='diamonds')
- Card(rank='8', suit='diamonds')
- Card(rank='4', suit='clubs')
- Card(rank='6', suit='diamonds')
- Card(rank='9', suit='clubs')
- Card(rank='K', suit='spades')
- Card(rank='4', suit='hearts')
- Card(rank='J', suit='diamonds')
- Card(rank='5', suit='diamonds')
- Card(rank='6', suit='clubs')
- Card(rank='A', suit='spades')
- Card(rank='9', suit='hearts')
- Card(rank='K', suit='diamonds')
- Card(rank='7', suit='clubs')
- Card(rank='A', suit='hearts')
- Card(rank='Q', suit='spades')
- Card(rank='A', suit='diamonds')
- Card(rank='J', suit='clubs')
- Card(rank='3', suit='spades')
- Card(rank='2', suit='hearts')
- Card(rank='7', suit='hearts')
- Card(rank='K', suit='hearts')
- 抽象基类:
- 抽象基类的作用类似于JAVA中的接口。在接口中定义各种方法,然后继承接口的各种类进行具体方法的实现。抽象基类就是定义各种方法而不做具体实现的类,任何继承自抽象基类的类必须实现这些方法,否则无法实例化。
- 那么抽象基类这样实现的目的是什么呢? 假设我们在写一个关于动物的代码。涉及到的动物有鸟,狗,牛。首先鸟,狗,牛都是属于动物的。既然是动物那么肯定需要吃饭,发出声音。但是具体到鸟,狗,牛来说吃饭和声音肯定是不同的。
- 需要具体去实现鸟,狗,牛吃饭和声音的代码。概括一下抽象基类的作用:定义一些共同事物的规则和行为。
- 来看下具体的代码实现,定义一个抽象基类的简单方法如下: 首先在Dog,Bird,Cow都继承自Animal。 在Animal中定义了eat和voice两个方法
- 任何从Animal中继承的子类都必须实现eat和voice方法。否则调用的时候会报错class Animal(object):
- def eat(self):
- raise NotImplementedError
- def voice(self):
- raise NotImplementedError
- class Dog(Animal):
- def voice(self):
- print 'wow....'
- class Bird(Animal):
- def voice(self):
- print 'jiji....'
- class Cow(Animal):
- def voice(self):
- print 'Oh.....'
- if __name__ == "__main__":
- d=Dog()
- d.voice()
- d.eat()
- 执行结果如下, voice可以正常执行,但是eat却报错了
- E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter11.py
- wow....
- Traceback (most recent call last):
- File "E:/py_prj/fluent_python/chapter11.py", line 54, in <module>
- d.eat()
- File "E:/py_prj/fluent_python/chapter11.py", line 33, in eat
- raise NotImplementedError
- NotImplementedError
- 这样实现有个缺点,就是只有子类调用eat方法的时候才会报错。子类是可以正常实例化的。但是你能够想象鸟,狗,牛不会吃饭么? 如果不会吃饭那肯定不算是动物了。所以正常的实现应该是如果没有实现eat方法,实例化就应该是失败的。那么这里就要用到抽象基类的一般使用方法.代码修改如下:
- Import abc
- class Animal(object):
- __metaclass__ = abc.ABCMeta
- @abc.abstractmethod
- def eat(self):
- return
- @abc.abstractmethod
- def voice(self):
- return
- if __name__ == "__main__":
- d=Dog()
- 结果如下,代码无法实例化,提示没有实现eat方法。这样就完美的达到了我们的目的。
- E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter11.py
- Traceback (most recent call last):
- File "E:/py_prj/fluent_python/chapter11.py", line 56, in <module>
- d=Dog()
- TypeError: Can't instantiate abstract class Dog with abstract methods eat
- 完整代码修改如下class Animal(object):
- __metaclass__ = abc.ABCMeta
- @abc.abstractmethod
- def eat(self):
- return
- @abc.abstractmethod
- def voice(self):
- return
- class Dog(Animal):
- def voice(self):
- print 'wow....'
- def eat(self):
- print 'Dog eat....'
- class Bird(Animal):
- def voice(self):
- print 'jiji....'
- def eat(self):
- print 'Bird eat....'
- class Cow(Animal):
- def voice(self):
- print 'Oh.....'
- def eat(self):
- print 'Cow eat....'
- if __name__ == "__main__":
- d=Dog()
- b=Bird()
- c=Cow()
- d.voice()
- d.eat()
- b.voice()
- b.eat()
- c.voice()
- c.eat()
- E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter11.py
- wow....
- Dog eat....
- jiji....
- Bird eat....
- Oh.....
- Cow eat....
- 除了继承,还有一种注册的方法可以将类和抽象基类关联起来:Animal.register(Cat)
- class Cat(object):
- def voice(self):
- print 'miao.....'
- def eat(self):
- print 'Cat eat....'
- Animal.register(Cat)
- if __name__ == "__main__":
- c=Cat()
- c.eat()
- c.voice()
- E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter11.py
- Cat eat....
- miao...
- 继承和注册这两种方法有什么区别呢:区别在于通过继承能够看到继承抽象基类的所有类,而用注册的方法却看不到。
- for sc in Animal.__subclasses__():
- print sc.__name__
- E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter11.py
- Dog
- Bird
- Cow
- 执行结果里面,只有Dog,Bird,Cow并没有Cat
- 最后介绍一种抽象子类的注册方式:__subclasshook__
- class Animal(object):
- __metaclass__ = abc.ABCMeta
- @abc.abstractmethod
- def eat(self):
- return
- @abc.abstractmethod
- def voice(self):
- return
- @classmethod
- def __subclasshook__(cls, c):
- if cls is Animal: :⑴
- if any("eat" in cat.__dict__ for cat in c.__mro__):⑵
- return True
- return NotImplementedError ⑶
- class Cat(object):
- def voice(self):
- print 'miao.....'
- def eat(self):
- print 'Cat eat....'
- if __name__ == "__main__":
- c=Cat()
- print isinstance(Cat(),Animal)
- print Animal.__subclasshook__(Cat)
- E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter11.py
- True
- True
- (1)首先判断cls是否属于Animal,在这里__subclasshook__被classmethod修饰,证明是一个对象的方法,因此cls肯定等于Animal
- (2)首先得到c.__mro__. 当调用isinstance(Cat(),Animal)或者Animal.__subclasshook__(Cat)的时候,c就是Cat,c.__mro__就是得到Cat以及Cat的父类。c.__mro__=(<class '__main__.Cat'>, <type 'object'>)。 然后看下在Cat以及Cat的父类object的属性中是否有eat方法的实现,这里可以用eat或者voice方法来判断。如果是,则返回True
- (3)否则返回NotImplementedError
流畅python学习笔记:第十一章:抽象基类的更多相关文章
- python cookbook第三版学习笔记十六:抽象基类
假设一个工程中有多个类,每个类都通过__init__来初始化参数.但是可能有很多高度重复且样式相同的__init__.为了减少代码.我们可以将初始化数据结构的步骤归纳到一个单独的__init__函数中 ...
- Python学习笔记(十一)
Python学习笔记(十一): 生成器,迭代器回顾 模块 作业-计算器 1. 生成器,迭代器回顾 1. 列表生成式:[x for x in range(10)] 2. 生成器 (generator o ...
- 【python学习笔记】7.更加抽象
[python学习笔记]7.更加抽象 类的定义就是执行代码块 在内存保存一个原始实例,可以通过类名来访问 类的实例化,是创建一个原始实例的副本, 并且所有成员变量与原始实例绑定 通过修改实例变量,可以 ...
- qml学习笔记(二):可视化元素基类Item详解(上半场anchors等等)
原博主博客地址:http://blog.csdn.net/qq21497936本文章博客地址:http://blog.csdn.net/qq21497936/article/details/78516 ...
- python面对对象编程---------6:抽象基类
抽象基本类的几大特点: 1:要定义但是并不完整的实现所有方法 2:基本的意思是作为父类 3:父类需要明确表示出那些方法的特征,这样在写子类时更加简单明白 用抽象基本类的地方: 1:用作父类 2:用作检 ...
- Python 接口:从协议到抽象基类
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px Helvetica } 抽象基类的常见用途:实现接口时作为超类使用.然后,说明抽象基类如何检查 ...
- #Python学习笔记:1-3章 (基于《python编程,从入门到实践)
第1-3章 这个文档是记录我学习python时一些学习笔记以及一些想法也可以称作复习笔记 第一章:起步这一章主要是从第一个"hello world"程序到python环境的搭建与配 ...
- [Python学习笔记][第七章Python文件操作]
2016/1/30学习内容 第七章 Python文件操作 文本文件 文本文件存储的是常规字符串,通常每行以换行符'\n'结尾. 二进制文件 二进制文件把对象内容以字节串(bytes)进行存储,无法用笔 ...
- [Python学习笔记][第五章Python函数设计与使用]
2016/1/29学习内容 第四章 Python函数设计与使用 之前的几页忘记保存了 很伤心 变量作用域 -一个变量已在函数外定义,如果在函数内需要修改这个变量的值,并将这个赋值结果反映到函数之外,可 ...
随机推荐
- 抽象工厂(AbstractFactory)模式-创建型模式
1.new 的问题 常见的对象创建方法: //创建一个Road对象 Road road=new Road(); new的问题:实现依赖,不能应对具体实例的变化 怎么理解上面这句话呢? 可以这样理解:我 ...
- nodejs中exports与module.exports的区别详细介绍
如果模块是一个特定的类型就用Module.exports.如果模块是一个典型的"实例化对象"就用exports. exports.name = function() { conso ...
- 咦,好像可以自己做个webapi框架了-IRouteHandler的使用
当我们学习到一定程度的时候,我们会想要去深入了解代码底层的东西,也更想拥有一个属于自己的框架,当然,博主也正是如此.本文可能成为编写一个webapi框架的开端.有研究MVC框架的朋友会发现,mvc框架 ...
- xssless - 自动化的XSS payload攻击器
XSSLESS 一个用Python编写的自动化XSS 负载(payload)攻击器 用法: 记录请求 并结合Burp proxy 选择你想生成的请求,然后右键选择“保存项目” 使用xssless生成你 ...
- django 自定义过滤器(filter)处理较为复杂的变量的实例
简述:django 在views中有数据需要通过字典(dict)的方式传递给template,该字典中又包含了字典,而且字典中的键值还是一个对象,在template中处理传递过来的数据的时候,字典不能 ...
- xtrabackup备份原理
Percona XtraBackup工作原理 Percona XtraBackup是基于InnoDB的崩溃恢复功能.复制InnoDB数据文件,导致内部不一致的数据; 但随后它对文件执行崩溃恢复,使它们 ...
- [转] (CQRS)命令和查询责任分离架构模式(二) 之 Command的实现
概述 继续引用上篇文章中的图片(来源于Udi Dahan博客),UI中的写入操作都将被封装为一个命令中,发送给Domain Model来处理. 我们遵循Domain Driven Design的设计思 ...
- PHP完成一个日历
今天我们就用php中的数组合date 做一个日历. 先让我们回顾一下PHP里面的date . 1.时间戳:表示从计算机元年/UNIX纪年(0时区 1970/1/1 00:00:00)到当前事件的秒数. ...
- java web项目中 读取properties 路径的问题
可以先获取项目的classPath String classPath = this.getClass().getResource("/").getPath();//获取classP ...
- org.apache.commons.lang3 的随机数生成
apache org.apache.commons.lang3 的随机数生成工具,方便使用. String a12 = RandomStringUtils.random(4, "012345 ...