"""
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. elasticsearch 通过外网访问

    elasticsearch 只能通过本地访问 需要修改  network.host: 0.0.0.0. 重新开启:提示错误: ERROR: [2] bootstrap checks failed[1] ...

  2. TileMap地图

    参考资料: http://8287044.blog.51cto.com/5179921/1045274 TileMap编辑器使用   1.认识TileMap     TileMap是一款开源的地图编辑 ...

  3. pem转pfx

    openssl req -new -key privkey.pem -out root.csr openssl x509 -req -days -sha1 -extensions v3_ca -sig ...

  4. CMFCPropertyGridProperty的使用

    设定初始值 CString str(_T("Button")); COleVariant cOlevariant(str); pTypeProperty->SetOrigin ...

  5. LeetCode--687. 最长同值路径

    题目描述:给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值.这条路径可以经过也可以不经过根节点.注意:两个节点之间的路径长度由它们之间的边数表示. 示例1:输入: 5 / \ 4 5 / ...

  6. Fuzzy and fun on Air Jordan 12 Doernbecher design

    Carissa Navarro keeps it warm, fuzzy and fun on her 2017 Air Jordan 12 Doernbecher design. Nike's 20 ...

  7. codefirst configuration

    codefirst 书写配置类,用于entity与数据库中表或view映射 1.对应表或视图都用this.ToTable("SimpleCompanyLoanView", &quo ...

  8. C/S模型之UDP协议

    说明:利用UDP协议,创建一个服务器和一个客户端.两者间进行通信.由客户端进行输入内容,而服务器将接受的内容进行再一次返回,并显示在服务端. // UDP_Seversock.cpp : 定义控制台应 ...

  9. LibSVM源码剖析(java版)

    之前学习了SVM的原理(见http://www.cnblogs.com/bentuwuying/p/6444249.html),以及SMO算法的理论基础(见http://www.cnblogs.com ...

  10. C++ Word Count 发布程序

    前段时间,模仿 Linux 系统下的 wc 程序,在 Windows 系统环境下使用 C/C++ 实现了一个相似的 WC 程序,只不过有针对性,针对的是 C/C++,Java 等风格的源代码文件. 此 ...