流畅的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是经常用到的结构 列表的推导,将一个字符串编程一个列表,有下面的2种方法.其中第二种方法更简洁.可读性也比第一种要好 str='abc' strin ...
- 流畅的python学习笔记:第一章
这一章中作者简要的介绍了python数据模型,主要是python的一些特殊方法.比如__len__, __getitem__. 并用一个纸牌的程序来讲解了这些方法 首先介绍下Tuple和nametup ...
- 流畅的python学习笔记第七章:装饰器
装饰器就如名字一样,对某样事物进行装饰过后然后返回一个新的事物.就好比一个毛坯房,经过装修后,变成了精装房,但是房子还是同样的房子,但是模样变了. 我们首先来看一个函数.加入我要求出函数的运行时间.一 ...
- 流畅的python学习笔记:第九章:符合python风格的对象
首先来看下对象的表现形式: class People(): def __init__(self,name,age): self.name=name self.a ...
- 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:用作检 ...
随机推荐
- ios大文件存储
I am using Erica Sadun's method of Asynchronous Downloads (link here for the project file: download) ...
- VS2010 MFC中制作Visual Studio风格的停靠侧栏窗口(CDockablePane里嵌套FormView表单视图)
VS2010 MFC中制作Visual Studio风格的停靠侧栏窗口(CDockablePane里嵌套FormView表单视图) 1. 在资源窗口里新建一个FormView的Dialog,修改ID为 ...
- Hadoop本地调试
windows上先调试该程序,然后再转到linux下. 程序运行的过程中, 报 Failed to locate the winutils binary in the hadoop binary pa ...
- vue2.X 自定义 模态框 modal
1.自定义 modal Modal.vue <!-- 模态框 --> <template> <div class="modal-mask" v-sho ...
- remove-duplicates-from-sorted-list I&II——去除链表中重复项
I.Given a sorted linked list, delete all duplicates such that each element appear only once. For exa ...
- 开发SharePoint 自定义WebService 的小工具
是一个开源的项目,地址:http://www.codeproject.com/Articles/10728/WSS-Web-Service-DISCO-and-WSDL-Generator-Helpe ...
- iOS SDK具体解释之UIDevice(系统版本号,设备型号...)
原创Blog,转载请注明出处 blog.csdn.net/hello_hwc 欢迎关注我的iOS SDK具体解释专栏 blog.csdn.net/column/details/huangwenchen ...
- Gson把对象转成json格式的字符串
近期在做一个java web service项目,须要用到jason,本人对java不是特别精通,于是開始搜索一些java平台的json类库. 发现了google的gson.由于之前对于protoco ...
- EMC机理------串扰
转:电子工程师不得不知道的EMC机理------串扰(韬略科技EMC) 串扰是信号完整性中最基本的现象之一,在板上走线密度很高时串扰的影响尤其严重.我们知道,线性无缘系统满足叠加定理,如果受害线上有信 ...
- EAI G4-lidar ROS配置
(1)使用命令创建 ydlidar_ws 工作空间,并将 G4 资料包内的 ROS 驱动包 ydlidar 下载到ydlidar_ws/src 目录下,切换到 ydlidar_ws 工作空间下并重新进 ...