之前也用Python写过爬虫,处理过数据,但是感觉理解还是挺浮于表面的,很多Python的特性都不是很了解。所以决定跟着一本书来帮助自己更深入地理解Python。最后选择了这本《流畅的Python》。在学习的过程中发现确实能学到很多东西,作者的讲解也非常深入浅出,很容易理解并且让人感到Python的魅力,译者的翻译也很好。
并且在读这本书的过程中,真的发现自己对Python的了解还太少,很多函数、特性都不清楚,需要查资料去理解,所以决定把学习过程中自己不知道的点记录下来,算是把书变厚的一个过程吧,争取学到更多,记忆更深!
第1章 Python数据模型
第一章是概览性质的内容。
因为在一开始读这本书的时候,发现第一章的代码里面就有很多不知道的函数、不知道的用法,一度觉得自己把书买的太深了,不适合自己读。但是读到第二章发现,第一章的很多内容在第二章会有详细的解释,所以跟我一样的小伙伴们千万不要被第一章吓到呀 : )
在对数据模型的解释中,作者提到:
数据模型其实是对Python框架的描述,它规范了这门语言自身构建模块的接口,这些模块包括不限于序列、迭代器、函数、类和上下文管理器。
接着作者就讲到了特殊方法(但是我并不是很明白数据模型和这个有什么关系,等以后理解了再修改吧):
Python解释器碰到特殊的句法时,会使用特殊方法去激活一些基本的对象操作,这些特殊方法的名字以两个下划线开头,以两个下划线结尾(例如__ getItem__)。比如obj[key]的背后就是__getItem__方法,为了能求得my_collection[key]的值,解释器实际会调用my_collection.__getItem__(key)。
1. 一摞Python风格的纸牌
作者通过这一节,实现__getItem__和__len__两个特殊方法,代码如下:
import collections
Card = collections.namedtuple('Card', ['rank', 'suit']) # 构建只有少数属性但没有方法的对象
class FrenchDeck:
ranks = [str(n) for n in range(2, 11)] + list('JQKA') # 牌2~10 + JQKA
suits = 'spades diamonds clubs hearts'.split() # 四种花色 黑桃 方片 梅花 红桃
def __init__(self):
# 13 * 4 = 52 张牌,笛卡尔积
self._cards = [Card(rank, suit) for suit in self.suits for rank in self.ranks]
# 自定义__len__方法
def __len__(self):
return len(self._cards)
def __getitem__(self, position):
return self._cards[position]
beer_card = Card('7', 'diamonds') # Card(rank='7', suit='diamonds')
deck = FrenchDeck()
len(deck) # 52
deck[0] # Card(rank='2', 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')]
# 切片,取第12个元素,后面每隔13个元素去一个[start: stop: step]
代码的主要内容在注释中已详述,主要包括:
(1)__len__特殊方法;
(2)列表推导(第二章详述);
(3)sorted/reversed方法(第二章详述sorted方法);
(4)切片。
2. 关于特殊方法的调用:
特殊方法的存在是为了被Python解释器调用的,并没有my_object.__len__()这种写法,而是应该使用len(my_object),在执行时,若my_object是一个自定义类的对象,那么Python会自己去调用其中自己实现的__len__方法。
通过下面的例子,实现一个二维向量类,来展示特殊方法的调用:
from math import hypot
class Vector:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
# 使用%r来获取对象各个属性的标准字符串表示形式
def __repr__(self):
return 'Vector(%r, %r)' % (self.x, self.y)
# 取模
def __abs__(self):
return hypot(self.x, self.y) # 返回欧几里得范数sqrt(x * x + y * y)
def __bool__(self):
return bool(abs(self))
# 通过__add__方法实现+运算符
def __add__(self, other):
x = self.x + other.x
y = self.y + other.y
return Vector(x, y)
# 通过__mul__方法实现*运算符
def __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
v1 = Vector(2, 4)
v2 = Vector(2, 1)
repr(v1) # 'Vector(2, 4)'
v1 + v2 # Vector(4, 5)
v1 * 2 # Vector(4, 8)
abs(v1) # 4.47213595499958
上面这段代码的主要内容包括:
(1)特殊方法的调用方法;
(2)hypot函数,获得欧几里得距离;
(3)使用特殊方法实现运算符。
最后作者还讨论了内置函数repr和str的联系与区别:
(1)repr函数通过__repr__这个特殊方法来得到一个对象的字符串表示形式,方便调试和记录日志;
(2)str()函数中使用__str__,或者在用print函数打印一个对象时才被调用,返回的字符串对终端用户更加友好。
大概第一章是内容就是这些,引入了特殊方法的概念,并通过两段代码来说明特殊方法的意义与使用。
第二章会有更加具体的内容。