"""
class Vector2d:
typecode = 'd' def __init__(self,x,y):
self.__x = float(x)
self.__y = float(y)
@property
def x(self):
return self.__x
@property
def y(self):
return self.__y
def __iter__(self):
return (i for i in (self.x,self.y))
from random import shuffle l = list(range(10))
shuffle(l)
print(l) #[1, 7, 0, 8, 5, 2, 9, 4, 6, 3]
tuple1 = (1,2)
#shuffle(tuple1)
'''
Traceback (most recent call last):
File "C:/Users/wangxue1/PycharmProjects/fluentPython/fromxieyiToChouxiangjilei/__init__22.py", line 21, in <module>
shuffle(tuple1)
File "C:\Python36\lib\random.py", line 274, in shuffle
x[i], x[j] = x[j], x[i]
TypeError: 'tuple' object does not support item assignmen
'''
#这个错误信息相当明确。问题原因是,shuffle函数要调换集合中元素的位置,而tuple只实现了不尅版序列协议。可变的序列还必须提供__setitem__方法。
#Python是动态语言,因此我们可以在运行时修正这个问题,甚至还可以在交互式控制台中,
#栗子11-6 为FrenchDeck打猴子补丁(运行时修改,不改源码),把他变成可变的,让random.shuffle函数能处理
from random import shuffle
import collections
Card = collections.namedtuple('Card',('rank','suit')) 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 rank in self.ranks for suit in self.suits]
def __len__(self):
return len(self._cards)
def __getitem__(self, item):
return self._cards[item] def set_card(deck,position,card):
deck._cards[position] = card FrenchDeck.__setitem__ = set_card
deck = FrenchDeck()
shuffle(deck)
print(deck[:5])
"""
#11.5 定义抽象基类的子类
#例子11-8 FrenchDeck2,collections.MutableSequence的子类
import collections
Card = collections.namedtuple('Card',['rank','suit']) class FrenchDeck2(collections.MutableSequence):
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 rank in self.ranks for suit in self.suits]
def __len__(self):
return len(self._cards)
def __getitem__(self, item):
return self._cards[item]
def __setitem__(self, key, value): #为了支持洗牌,只需支持这个方法
self._cards[key] = value
def __delitem__(self, key): #继承MutableSequence必须实现这个方法,这是基类的一个抽象方法
del self._cards[key]
#def insert(self, index, value): #这是继承的类的第三个抽象方法
# self._cards.insert(index,value)
#
#a = FrenchDeck2() #如果将上方任意一个实现的基类的抽象方法注释掉,则会报错;TypeError: Can't instantiate abstract class FrenchDeck2 with abstract methods insert。
#导入时(加载并编译),python不会检查抽象方法的实现,在运行时实例化FrenchDeck2类时才会真正检查。
#因为这个原因,即使FrenchDeck2类不需要__delitem__和insert提供的行为,也要实现,因为MulableSequence抽象基类需要它们
#要想实现子类,可以覆盖从抽象基类中继承的方法,以更高效的方式重新实现。例如,__contains__方法会全面扫描序列,可是,如果你定义的序列按顺序保存元素,那就可以重新定义__contains__方法,使用bisect函数做二分查找,从而提升搜索速度 #11.6 标准库中的抽象基类
#大多数抽象基类在collections.abc模块中定义,不过其他地方也有。例如,numbers和io包中有一些抽象基类。但是,前者更常用
#11.6.1 collections.abc模块中的抽象基类,这个模块中定义了16个抽象基类
#简述这16个抽象基类
#Iterable Container Sized :各个集合应该继承这三个抽象基类,或者至少实现兼容的协议。Iterable通过__iter__方法支持迭代,Container通过__contains__方法支持in运算符、Sized痛殴__len__方法支持len()函数
#Sequence Mapping Set:主要是不可变类型,而且各自都有可变的子类。MutableSequence MutableMapping MutableSet
#MappingView :映射方法 .item() .keys() .values()返回的对象分别是ItemsView KeysView ValuesView 。前两个类还从Set 类继承了丰富的接口
#Callable Hashble :这两个抽象类与集合没有太大关系,只不过因为Collections.abc是标准库中定义抽象基类的第一个模块,而它们又太重要了,因此才把他们放在Collections.abc模块中。这两个主要为内置函数isinstance提供支持,以一种安全
#...的方式判断对象能不能被调用或散列。
# Iterator :注意他是Iterable 的子类。
#11.6.2 抽象基类的数字塔
#numbers包定义的数字塔,从塔尖到塔底分别是:Number Complex Real Rational Integral
#想要检查一个数是不是整数,可以使用isinstance(x,numbers.Integral),这样代码就能接收int bool (int的子类),或者外部库使用numbers抽象基类注册的其他类型。
#若要检查一个数是不是浮点数,可以使用isinstance(x,numbers.Real)。这样代码就能接收bool int float fractions.Fraction ,或者外部库(NumPy,它作了注册)提供的非复数类型
#...decimal.Decimal没有注册为numbers.Real的虚拟子类,这有点奇怪。没注册的原因是,如果你的程序需要Decimal的精度,要防止与其他低精度数字类型混淆,尤其是浮点数 #下面我们将从零开始实现一个抽象基类,然后实际使用,以此实践白鹅类型呢。这么做的目的不是鼓励每个人都立即开始定义抽象基类,而是教授怎么阅读标准库和其他包中的抽象基类源码 #11.7 定义并使用一个抽象基类
#例子11-9 抽象类,有两个抽象方法和两个具体方法
import abc class Tombola(abc.ABC): #自己定义的抽象类要继承abc.ABC
@abc.abstractmethod
def load(self,iterable): #抽象方法,定义体中通常只有文本字符串
"""从可迭代对象中添加元素""" @abc.abstractmethod
def pick(self):
"""随机删除元素,然后将其返回 如果实例为空,应抛出LookupError
""" def loaded(self):
"""如果至少有一个元素,应该返回True,否则返回False"""
return bool(self.inspect()) #抽象基类中的具体方法只能一来抽象基类定义的接口(即只能使用抽象基类中的其他具体方法、抽象方法或特性) def inspect(self):
"""返回一个有序元祖,由当前元素构成"""
items = []
while True: #我们不清楚具体子类如何存储元素,不过为了得到inpect结果,可以不断调用.pick(),把Tombla清空......
try:
items.append(self.pick())
except LookupError:
break
self.load(items) #......然后再使用.load()把所有元素放进去
return tuple(sorted(items))
#抽象方法可以有实现代码,即便实现了,子类也必须覆盖抽象方法,但是在子类中可以使用super()函数调用抽象方法,为它添加功能,而不是从头开始实现。
#这里的.inspect()实现的方式有些笨拙,不过此例是像强调抽象基类可以提供具体方法,只要一来接口中的其他方法就系那个。
#.loaded()方法没那么笨拙,但是耗时,
#选择使用LookupError异常的原因,在python的异常层次关系中,它与IndexError 和 KeyError有关,这两个是具体实现Tombola所用的数据结构最有可能抛出的异常。据此,实现代码可能会抛出LookupError IndexError KeyError.
#...后两者是前者的子类,所以直接抛出LookupError
#IndexError是LookupError的子类,尝试从序列中获取索引超过最后位置的元素时抛出
#使用不存在的键从映射中获取元素时,抛出KeyError
#除了@abstractmethod之外,abc模块还定义了@abstractclassmethod @abstractstaticmethod @abstractproperty 三个装饰器。然而,后三个装饰器从python3.3起废弃了,因为装饰器可以在@abstractmethod上堆叠,那三个就显得多余了。
#...例如,声明抽象类方法的推荐方式
'''
class MyABC(abc.ABC):
@classmethod
@abc.abstractmethod
def an_abstract_classmethod(cls,...):
pass
'''
#在函数上堆叠装饰器的顺序通常很重要,和其他方法描述符仪器使用时,abstractmethod()应放在最里层,也就是说,在@abstractmethod和def语句中间不能有其他装饰器 #11.7.2 定义Tombola抽象基类的子类
#;哦走11-12 BingoCage是Tomlola的具体子类
import random
class BingoCage(Tombola):
def __init__(self,items):
self._randomizer = random.SystemRandom() #假设我们将在线上游戏中使用这个。random.SystemRandom 使用os.urandom()函数实现random API。os.urandom()生成"适合用于加密"的随机字节序列
self._items= []
self.load(items) def load(self,items):
self._items.extend(items)
self._randomizer.shuffle(self._items) # 没有使用random.shuffle()函数,而是使用SystemRandom实例的.shuffle()方法 def pick(self):
try:
return self._items.pop()
except IndexError:
raise LookupError('pick from empty BingoCage') def __call__(self):
self.pick() #例子11-13 LotterBlower是Tombola的具体子类,覆盖了inspect 和 loaded方法
class LotteryBlower(Tombola):
def __init__(self,iterable):
self._balls = list(iterable)
def load(self,iterable):
self._balls.extend(iterable)
def pick(self):
try:
position = random.randrange(len(self._balls)) #如果范围为空,random.randrange()函数抛出ValueError,为了兼容Tombola,我们捕获它,抛出LookupError
except ValueError:
raise LookupError('pick from empty LotterBlower')
return self._balls.pop(position)
def loaded(self): #覆盖loaded方法,避免调用inspect方法。可以直接处理self._balls而不必构建整个有序元祖,从而提升速度
return bool(self._balls) def inspect(self):
return tuple(sorted(self._balls))
#上方例子,有个习惯做法值得指出:在__init__方法中,self._balls保存的是list(iterable),而不是iterable的引用(即没有直接把iterable赋值给self._balls),前面说过,这样使这个类更灵活,因为iterable参数可以是任何可迭代的类型
#...把元素存入列表中还能确保取出元素。就算iterable参数之中传入列表,list(iterable)会创建参数的副本。这依然是好的做法,因为我们要从中删除元素,而客户可能不希望自己提供的列表被修改。 #11.7.3 Tombola的虚拟子类
#白鹅类型的一个基本特性:即便不继承,也有办法把一个类注册为抽象基类的虚拟子类。这样做时,我们保证注册的类忠实的实现了抽象基类定义的接口,而python会相信我们,从而不做检查。如果我们说谎了,那么常规的运行实践异常会把我们捕获
#注册虚拟子类是在抽象基类上调用register方法。这么做之后,注册的类会变成抽象基类的虚拟子类,而且issbuclass isinstance 等函数都能识别,但是注册的类不会从抽象基类中继承任何方法或属性
#虚拟子类不会继承注册的抽象基类,而且任何时候都不会检查它是否符合抽象基类的接口,即便在实例化时也不会检查。为了避免运行时错误,虚拟子类要实现所需的全部方法
#例子11-14
from random import randrange
@Tombola.register
class TomboList(list):
def pick(self):
if self: #从list中继承__bool__方法,列表不为空时返回True
position = randrange(len(self))

【Python】【元编程】【从协议到抽象基类】的更多相关文章

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

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

  2. Python抽象基类:ABC谢谢你,因为有你,温暖了四季!

    Python抽象基类:ABC谢谢你,因为有你,温暖了四季! Python抽象基类:ABC谢谢你,因为有你,温暖了四季! 实例方法.类方法和静态方法 抽象类 具名元组 参考资料 最近阅读了<Pyt ...

  3. Fluent_Python_Part4面向对象,11-iface-abc,协议(接口),抽象基类

    第四部分第11章,接口:从协议到抽象基类(重点讲抽象基类) 接口就是实现特定角色的方法集合. 严格来说,协议是非正式的接口(只由文档约束),正式接口会施加限制(抽象基类对接口一致性的强制). 在Pyt ...

  4. Python抽象基类之声明协议

    抽象基类之--声明协议 上回讲了Python中抽象基类的大概,相信大家对abcmeta以及什么是抽象基类已经有所了解.传送门 现在我们来讲讲抽象基类的另一个常用用法--声明协议 所谓声明协议,有点像J ...

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

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

  6. python(七):元类与抽象基类

    一.实例创建 在创建实例时,调用__new__方法和__init__方法,这两个方法在没有定义时,是自动调用了object来实现的.python3默认创建的类是继承了object. class A(o ...

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

    __getitem__实现可迭代对象.要将一个对象变成一个可迭代的对象,通常都要实现__iter__.但是如果没有__iter__的话,实现了__getitem__也可以实现迭代.我们还是用第一章扑克 ...

  8. Python元编程

    简单定义"元编程是一种编写计算机程序的技术,这些程序可以将自己看做数据,因此你可以在运行时对它进行内审.生成和/或修改",本博参考<<Python高级编程>> ...

  9. guxh的python笔记七:抽象基类

    1,鸭子类型和白鹅类型 1.1,白鹅类型 白鹅类型对接口有明确定义,比如不可变序列(Sequence),需要实现__contains__,__iter__,__len__,__getitem__,__ ...

随机推荐

  1. 十天精通CSS3(4)

    text-overflow 与 word-wrap text-overflow用来设置是否使用一个省略标记(...)标示对象内文本的溢出. 语法: 但是text-overflow只是用来说明文字溢出时 ...

  2. edgeR使用学习【转载】

    转自:http://yangl.net/2016/09/27/edger_usage/ 1.Quick start 2. 利用edgeR分析RNA-seq鉴别差异表达基因: #加载软件包 librar ...

  3. [LeetCode] 207 Course Schedule_Medium tag: BFS, DFS

    There are a total of n courses you have to take, labeled from 0 to n-1. Some courses may have prereq ...

  4. Bootstrap学习笔记-栅格系统

    栅格系统的原理就是在这个界面中这个栅格被分成12个格子,你根据自己的想要的布局就把这个界面分割成你想要的部分就行了.一般如果我们用电脑作为显示器的我们用的样式是col-md 如果你用的显示期变小的情况 ...

  5. SQLServer 重启服务后,自增1的标识列一次增长了1000(转自博问)

    sql2012:我重启了下sql服务,然后自增列Id居然一下子跳了100,怎么回事啊?(之前的数据Id为1,我重启服务后,第二条数据Id就变成1001了),我自增是1,求大神帮忙啊 SQLServer ...

  6. MFC六大核心机制之五、六:消息映射和命令传递

    作为C++程序员,我们总是希望自己程序的所有代码都是自己写出来的,如果使用了其他的一些库,也总是千方百计想弄清楚其中的类和函数的原理,否则就会感觉不踏实.所以,我们对于在进行MFC视窗程序设计时经常要 ...

  7. HDU 6390 GuGuFishtion

    题意: 计算: \[\sum\limits_{a = 1}^{m}\sum\limits_{b = 1}^{n} \frac{\varphi(ab)}{\varphi(a)\varphi(b)} (\ ...

  8. git参考文档

    ==================================================================================================== ...

  9. Python: 反方向迭代一个序列

    使用内置的reversed()函数 >>> a = [1, 2, 3, 4] >>> for x in reversed(a): ... print(x) out ...

  10. Python: 序列: 过滤序列元素

    问题: 你有一个数据序列,想利用一些规则从中提取出需要的值或者是缩短序列 answer: eg1:列表推导 最简单的过滤序列元素的方法就是使用列表推导.比如:>>> mylist = ...