流畅的python学习笔记:第十一章:抽象基类

mac2022-06-30  74

__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 = intfor 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 NotImplementedErrorclass 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

转载于:https://www.cnblogs.com/zhanghongfeng/p/7220533.html

最新回复(0)