第1章 Python数据模型
#《流畅的Python》读书笔记
# 第一部分 序幕
# 第1章 Python数据模型
# 魔术方法(magic method)是特殊方法的昵称。于是乎,特殊方法也叫双下方法(dunder method)。 # 1.1 一摞Python风格的纸牌
# 示例 1-1 一摞有序的纸牌
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 suit in self.suits for rank in self.ranks] def __len__(self):
return len(self._cards) def __getitem__(self, position):
return self._cards[position] # 如下面这个控制台会话所示,利用namedtuple,我们可以很轻松地得到一个纸牌对象:
>>> beer_card=Card('','diamond')
>>> beer_card
Card(rank='', suit='diamond') # 首先,它跟任何标准Python集合类型一样,可以用len()函数来查看一叠牌有多少张:
>>> deck=FrenchDeck()
>>> len(deck)
52 #从一叠牌中抽取特定的一张纸牌,比如说第一张或最后一张,是很容易的:deck[0]或deck[-1]。这都是由__getitem__方法提供的:
>>> deck[0]
Card(rank='', suit='spades')
>>> deck[-1]
Card(rank='A', suit='hearts') #Python 已经内置了从一个序列中随机选出一个元素的函数random.choice
>>> from random import choice
>>> choice(deck)
Card(rank='J', suit='hearts')
>>> choice(deck)
Card(rank='', suit='clubs')
>>> choice(deck)
Card(rank='', suit='diamonds') #下面列出了查看一摞牌最上面3张和只看牌面是A的牌的操作。
>>> deck[:3]
[Card(rank='', suit='spades'), Card(rank='', suit='spades'), Card(rank='', suit='spades')]
>>> deck[12::13]
[Card(rank='A', suit='spades'), Card(rank='A', suit='diamonds'), Card(rank='A', suit='clubs'), Card(rank='A', suit='hearts')] #另外,仅仅实现了__getitem__方法,这一摞牌就变成可迭代的了:
>>> for card in deck:
print(card)
Card(rank='', suit='spades')
Card(rank='', suit='spades')
Card(rank='', suit='spades')
Card(rank='', suit='spades')
... #反向迭代也没关系:
>>> for card in reversed(deck):
print(card)
Card(rank='A', suit='hearts')
Card(rank='K', suit='hearts')
Card(rank='Q', suit='hearts')
Card(rank='J', suit='hearts')
... #in运算符就会按顺序做一次迭代搜索
>>> Card('Q','hearts') in deck
True
>>> Card('','beats') in deck
False #梅花2的大小是0,黑桃A是51:
>>> suit_values=dict(spades=3,hearts=2,diamonds=1,clubs=0)
>>> def spades_high(card):
rank_value=FrenchDeck.ranks.index(card.rank)
return rank_value*len(suit_values)+suit_values[card.suit] #有了spades_high函数,就能对这摞牌进行升序排序了:
>>> for card in sorted(deck,key=spades_high):
print(card)
Card(rank='', suit='clubs')
Card(rank='', suit='diamonds')
Card(rank='', suit='hearts')
Card(rank='', suit='spades')
Card(rank='', suit='clubs')
... # 1.2 如何使用特殊方法
# 首先明确一点,特殊方法的存在是为了被Python解释器调用的,你自己并不需要调用它们。也就是说没有my_object.__len__()这种写法,而应该使用len(my_object)。在执行len(my_object)的时候,如果my_object是一个自定义类的对象,那么Python会自己去调用其中由你实现的__len__方法。
# 很多时候,特殊方法的调用是隐式的,比如for i in x:这个语句,背后其实用的是iter(x),而这个函数的背后则是x.__iter__()方法。当然前提是这个方法在x中被实现了。 # 通常你的代码无需直接使用特殊方法。除非有大量的元编程存在,直接调用特殊方法的频率应该远远低于你去实现它们的次数。唯一的例外可能是__init__方法,你的代码里可能经常会用到它,目的是在你自己的子类的__init__方法中调用超类的构造器。 # 通过内置的函数(例如len、iter、str,等等)来使用特殊方法是最好的选择。这些内置函数不仅会调用特殊方法,通常还提供额外的好处,而且对于内置的类来说,它们的速度更快。14.12节中有详细的例子。 # 不要自己想当然地随意添加特殊方法,比如__foo__之类的,因为虽然现在这个名字没有被Python内部使用,以后就不一定了。 #示例 1-2 一个简单的二维向量类
from math import hypot class Vector: def __init__(self,x=0,y=0):
self.x=x
self.y=y def __repr__(self):
return 'Vector(%r,%r)'%(self.x,self.y) def __abs__(self):
return hypot(self.x+self.y) # 如果想让Vector.__bool__更高效,可以采用这种实现:
# def __bool__(self):
# return bool(self.x or self.y) def __bool__(self):
return bool(abs(self)) def __add__(self, other):
x=self.x+other.x
y=self.y+other.y def __mul__(self,scalar):
return Vector(self.x*scalar,self.y*scalar) #为了给这个类型设计API,我们先写个模拟的控制台会话来做doctest. #下面这一段代码就是向量加法:
>>> v1 = Vector(2, 4)
>>> v2 = Vector(2, 1)
>>> v1 + v2
Vector(4, 5) #为了保持一致性,我们的API在碰到abs函数的时候,也应该返回该向量的模:
>>> v = Vector(3, 4)
>>> abs(v)
5.0 #我们还可以利用*运算符来实现向量的标量乘法:
>>> v * 3
Vector(9, 12)
>>> abs(v * 3)
15.0 # Python有一个内置的函数叫repr,它能把一个对象用字符串的形式表达出来以便辨认,这就是“字符串表示形式”。
# repr就是通过 __repr__这个特殊方法来得到一个对象的字符串表示形式的。
# 如果没有实现__repr__,当我们在控制台里打印一个向量的实例时,得到的字符串可能会是<Vector object at 0x10e100070>。
# __repr__和__str__的区别在于,后者是在str()函数被使用,或是在用print函数打印一个对象的时候才被调用的,并且它返回的字符串对终端用户更友好。 # 1.3 特殊方法一览
# 表1-1:跟运算符无关的特殊方法
# 表1-2:跟运算符相关的特殊方法 # 1.4 为什么len不是普通方法
# 换句话说,len之所以不是一个普通方法,是为了让Python自带的数据结构可以走后门,abs也是同理。
# 但是多亏了它是特殊方法,我们也可以把len用于自定义数据类型。
# 这种处理方式在保持内置类型的效率和保证语言的一致性之间找到了一个平衡点,也印证了“Python之禅”中的另外一句话:“不能让特例特殊到开始破坏既定规则。” # 1.5 本章小结
# 通过实现特殊方法,自定义数据类型可以表现得跟内置类型一样,从而让我们写出更具表达力的代码——或者说,更具Python风格的代码。 # Python对象的一个基本要求就是它得有合理的字符串表示形式,我们可以通过__repr__和__str__来满足这个要求。前者方便我们调试和记录日志,后者则是给终端用户看的。这就是数据模型中存在特殊方法__repr__和__str__的原因。 # 对序列数据类型的模拟是特殊方法用得最多的地方,这一点在FrenchDeck类的示例中有所展现。在第2章中,我们会着重介绍序列数据类型,然后在第10章中,我们会把Vector类扩展成一个多维的数据类型,通过这个练习你将有机会实现自定义的序列。 # Python通过运算符重载这一模式提供了丰富的数值类型,除了内置的那些之外,还有decimal.Decimal和fractions.Fraction。这些数据类型都支持中缀算术运算符。在第13章中,我们还会通过对Vector类的扩展来学习如何实现这些运算符,当然还会提到如何让运算符满足交换律和增强赋值。 #Python数据模型的特殊方法还有很多,本书会涵盖其中的绝大部分,探讨如何使用和实现它们。 # 1.6 延伸阅读
第1章 Python数据模型的更多相关文章
- 流畅的python第一章python数据模型学习记录
python中有些特殊的方法,以双上下划线开头,并以双下划线结束的方法.如__getitem__,这些方法是特殊的方法,供python解释权内部使用,一般来说不需要调用 还有一种是以双下划线开头的,如 ...
- 《流畅的Python》 第一部分 序章 【数据模型】
流畅的Python 致Marta,用我全心全意的爱 第一部分 序幕 第一章 Python数据模型 特殊方法 定义: Python解释器碰到特殊句法时,使用特殊方法激活对象的基本操作,例如python语 ...
- [Python学习笔记][第七章Python文件操作]
2016/1/30学习内容 第七章 Python文件操作 文本文件 文本文件存储的是常规字符串,通常每行以换行符'\n'结尾. 二进制文件 二进制文件把对象内容以字节串(bytes)进行存储,无法用笔 ...
- [Python学习笔记][第五章Python函数设计与使用]
2016/1/29学习内容 第四章 Python函数设计与使用 之前的几页忘记保存了 很伤心 变量作用域 -一个变量已在函数外定义,如果在函数内需要修改这个变量的值,并将这个赋值结果反映到函数之外,可 ...
- [Python学习笔记][第四章Python字符串]
2016/1/28学习内容 第四章 Python字符串与正则表达式之字符串 编码规则 UTF-8 以1个字节表示英语字符(兼容ASCII),以3个字节表示中文及其他语言,UTF-8对全世界所有国家需要 ...
- [Python笔记][第四章Python正则表达式]
2016/1/28学习内容 第四章 Python字符串与正则表达式之正则表达式 正则表达式是字符串处理的有力工具和技术,正则表达式使用预定义的特定模式去匹配一类具有共同特征的字符串,主要用于字符串处理 ...
- [Python笔记][第二章Python序列-复杂的数据结构]
2016/1/27学习内容 第二章 Python序列-复杂的数据结构 堆 import heapq #添加元素进堆 heapq.heappush(heap,n) #小根堆堆顶 heapq.heappo ...
- [Python笔记][第二章Python序列-tuple,dict,set]
2016/1/27学习内容 第二章 Python序列-tuple tuple创建的tips a_tuple=('a',),要这样创建,而不是a_tuple=('a'),后者是一个创建了一个字符 tup ...
- [python笔记][第二章Python序列-list]
2016/1/27学习内容 第二章 Python序列-list list常用操作 list.append(x) list.extend(L) list.insert(index,x) list.rem ...
随机推荐
- 【python中调用shell命令使用PIPE】使用PIPE作为stdout出现假卡死的情况——将stdout重定向为输出到临时文件
在Python中,调用:subprocess.Popen(cmd, stdout = PIPE, stderr = PIPE, shell= true)的时候,如果调用的shell命令本身在执行之后会 ...
- [剑指Offer]59-队列的最大值(题目二待补)
题目一:滑动窗口的最大值 题目链接 https://www.nowcoder.com/practice/1624bc35a45c42c0bc17d17fa0cba788?tpId=13&tqI ...
- redis.conf配置详解(转)
# redis 配置文件示例 # 当你需要为某个配置项指定内存大小的时候,必须要带上单位,# 通常的格式就是 1k 5gb 4m 等酱紫:## 1k => 1000 bytes# 1kb =&g ...
- 类似No module named 'bs4'等错误的解决方法
最近开始接触爬虫,写了如下源代码: from bs4 import BeautifulSoup import requests url='https://www.tripadvisor.cn/Attr ...
- mysql5.7 生成列 generated column
生成列的值是根据列定义中的表达式计算得出的. mysql5.7支持两种类型的生成列: 1.virtual 生成列:当从表中读取记录时,才计算该列值.不会把数据持久化在硬盘上. 2.stored 生成列 ...
- pom.xml中坐标的组成
坐标=组织(也就是所谓的公司名称)+项目名称+版本(如果不加范围默认为compile)
- vue-form表单验证插件
参考地址:https://segmentfault.com/q/1010000003988864 github地址:https://github.com/fergaldoyle/vue-form 安装 ...
- windbg 边学边记attach 进程和open dump的两个方式查看线程的占用cpu资源
首先我是attach到进程的方式,附加到进程把. vs里边有个远程调试就是通过连接到远程机附加到进程操作的.在 有公网IP情况下挺好用,但涉及到nat穿越之类的,因为用户的不方便设置,这种调试方式也有 ...
- linux 输出重定向
输出重定向 标准输入 文件描述符:0 设备:键盘 设备文件名:/dev/stdin 标准输出 文件描述符:1 设备:显示器 设备文件名:/dev/sdtout 标准输出重定向 命令 >> ...
- Java中方法的重写
★★前提:方法的重写建立在继承关系上★★ 在Java程序中,类的继承关系可以产生一个子类,子类继承父类,它具备了父类所有的特征,继承了父类所有的方法和变量. 所谓方法的重写是指子类中的方法与父类中继承 ...