Python 最好的品质之一是一致性。
魔术方法(magic method)是特殊方法的昵称。特殊方法也叫双下方法。

1.1 一摞Python风格的纸牌

 import collections
Card = collections.namedtuple('Card', ['rank', 'suit']) # 创建了一个有名字的元组 class FrenchDeck: # 隐式继承了Object类
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): # 调用 len(deck) 时,实际上是执行 len.__len__ 方法
return len(self._cards) def __getitem__(self, position): # 调用 deck[0] 时,实际上是执行 deck.__getitem__(key=0)
return self._cards[position] deck = FrenchDeck()
print(len(deck)) # 判断个数的定义,是由__len__实现的
print(deck[0]) # 根据位置抽取,此方法是由__getitem__实现的

通过实现特殊方法来利用 Python 数据模型的两个好处 :

1. 作为你的类的用户,他们不必去记住标准操作的各式名称(“怎么得到元素的总数? 是 .size() 还是 .length() 还是别的什么? ”)。
2. 可以更加方便地利用 Python 的标准库,比如 random.choice 函数,从而不用重新发明轮子。

同时,__getitem__ 方法把  [ ]  操作交给了 self._cards 列表,所以我们的 deck 类自动支持切片(slicing)操作。 另外,仅仅实现了 __getitem__ 方法,这一摞牌就变成可迭代的了 。

迭代通常是隐式的,譬如说一个集合类型没有实现 __contains__ 方法,那么 in 运算符就会按顺序做一次迭代搜索。

对牌堆进行排序:

 # 对牌堆排序
suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0) def spades_high(card):
rank_value = FrenchDeck.ranks.index(card.rank)
'''
card.rank:某张牌对象的rank属性(2-A),FrenchDeck.ranks.index(card.rank)返回该属性的位置
len(suit_values): 花色的种类,也就是4
suit_values[card.suit]: 就是根据某张牌对象的花色,取出该花色对应的值(权重)
'''
return rank_value * len(suit_values) + suit_values[card.suit] # 返回这张排在牌堆中的序号(唯一) for card in sorted(deck, key=spades_high):
print(card)

如何洗牌

  按照目前的设计, FrenchDeck 是不能洗牌的,因为这摞牌是不可变的(immutable):卡牌和它们的位置都是固定的,除非我们破坏这个类的封装性,直接对 _cards 进行操作。第 11 章会讲到,其实只需要一行代码来实现 __setitem__方法,洗牌功能就不是问题了。

1.2 如何使用特殊方法

  首先明确一点,特殊方法(双下方法)的存在是为了被 Python 解释器调用的,你自己并不需要调用它们。

  然而如果是 Python 内置的类型,比如列表(list)、字符串(str)、字节序列(bytearray)等,那么 CPython 会抄个近路, __len__ 实际上会直接返回 PyVarObject 里的 ob_size 属性。 PyVarObject 是表示内存中长度可变的内置对象的 C语言结构体。直接读取这个值比调用一个方法要快很多。
  很多时候,特殊方法的调用是隐式的,比如 for i in x: 这个语句,背后其实用的是iter(x),而这个函数的背后则是 x.__iter__() 方法。当然前提是这个方法在 x 中被实现了。
  通常你的代码无需直接使用特殊方法。除非有大量的元编程(meta programming)存在,直接调用特殊方法的频率应该远远低于你去实现它们的次数。唯一的例外可能是 __init__ 方法,你的代码里可能经常会用到它,目的是在你自己的子类的 __init__ 方法中调用超类的构造器。通过内置的函数(例如 len、 iter、 str,等等)来使用特殊方法是最好的选择。这些内置函数不仅会调用特殊方法,通常还提供额外的好处,而且对于内置的类来说,它们的速度更快。 14.12 节中有详细的例子。不要自己想当然地随意添加特殊方法,因为虽然现在这个名字没有被 Python 内部使用,以后就不一定了。

 from math import hypot

 class Vector:
def __init__(self, x=0, y=0):
self.x = x
self.y = y '''
一个对象用字符串的形式表达出来以便辨认,如果没有实现 __repr__,当我们在控制台里打印一个向量的实例时,得到的字符串可能会是 <Vector object at 0x10e100070>。
'''
def __repr__(self):
return 'Vector(%r, %r)' % (self.x, self.y)
# __repr__方便我们调试和记录日志,
# __str__是给终端用户用的 def __abs__(self):
return hypot(self.x, self.y) # 模是 0 就返回 False,其他返回 True
def __bool__(self):
# return bool(abs(self))
return bool(self.x or self.y) # 高效写法 # + 操作
def __add__(self, other):
x = self.x + other.x
y = self.y + other.y
return Vector(x, y) # * 操作
def __mul__(self, other):
return Vector(self.x * other, self.y * other)

  如果一个对象没有 __str__ 函数,而 Python 又需要调用它的时候,解释器会用 __repr__ 作为替代。

  

流畅的Python (Fluent Python) —— 第一部分的更多相关文章

  1. 流畅的Python (Fluent Python) —— 第二部分01

    2.1 内置序列类型概览 Python 标准库用 C 实现了丰富的序列类型,列举如下. 容器序列 list. tuple 和 collections.deque 这些序列能存放不同类型的数据. 扁平序 ...

  2. 流畅的Python (Fluent Python) —— 前言

    本书重点: 这本书并不是一本完备的 Python 使用手册,而是会强调 Python 作为编程语言独有的特性,这些特性或者是只有 Python 才具备的,或者是在其他大众语言里很少见的. Python ...

  3. 从Python小白到第一个小游戏发布

    1.安装必要的环境(附图两张) 直接下载安装程序,本人win10系统,根据电脑系统下载并安装对应的python.exe,安装路径可以选择D盘的,具体安装细节这里就不说了,不知道的可以留言或者找度娘 2 ...

  4. 「Fluent Python」今年最佳技术书籍

    Fluent Python 读书手记 Python数据模型:特殊方法用来给整个语言模型特殊使用,一致性体现.如:__len__, __getitem__ AOP: zope.inteface 列表推导 ...

  5. Python深入学习之《Fluent Python》 Part 1

    Python深入学习之<Fluent Python> Part 1 从上个周末开始看这本<流畅的蟒蛇>,技术是慢慢积累的,Python也是慢慢才能写得优雅(pythonic)的 ...

  6. Fluent Python: Classmethod vs Staticmethod

    Fluent Python一书9.4节比较了 Classmethod 和 Staticmethod 两个装饰器的区别: 给出的结论是一个非常有用(Classmethod), 一个不太有用(Static ...

  7. Fluent Python: Mutable Types as Parameter Defaults: Bad Idea

    在Fluent Python一书第八章有一个示例,未看书以先很难理解这个示例运行的结果,我们先看结果,然后再分析问题原因: 定义了如下Bus类: class Bus: def __init__(sel ...

  8. 跟着老男孩教育学Python开发【第一篇】:初识Python

    Python简介 Python前世今生 Python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解 ...

  9. Python开发【第一篇】:初识Python

    初识python 一.python简介 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解 ...

随机推荐

  1. Spring Boot教程(十四)快速入门

    快速入门 本章主要目标完成Spring Boot基础项目的构建,并且实现一个简单的Http请求处理,通过这个例子对Spring Boot有一个初步的了解,并体验其结构简单.开发快速的特性. 系统要求: ...

  2. leetcode-mid-sorting and searching - 56 Merge Intervals

    mycode 出现的问题:比如最后一个元素是[1,10],1小于前面所有元素的最小值,10大于前面所有元素的最大值,而我最开始的思路只考虑了相邻 参考: 思路:如果我只考虑相邻,必须先将list排序, ...

  3. eigen 四元数进行坐标旋转

    (<视觉SLAM十四讲>第三讲习题7)设有小萝卜一号和二号在世界坐标系中.一号位姿q1 = [0.35, 0.2, 0.3, 0.1],t1=[0.3, 0.1, 0.1].二号位姿q2= ...

  4. JS去重-删除连续重复的值

    function removeRepetition(str) { var result = "", unStr; for(var i=0,len=str.length;i<l ...

  5. PHP 数组函数 内部指针

    current( &$arr ) 每个数组的当前单元,初始值的 数组的第一个单元next ( &$arr ) 返回数组中的下一个单元 , 如果没值则返回falshprev ( & ...

  6. python接口自动化:绕过验证码登录

    上线产品的登录接口会有验证码,一般可以通过添加cookie的方式绕过验证码. 一.抓登录的cookie 1. 先手动登录一次,然后用fiddler抓取这个cookie,再直接把这个值添加到cookie ...

  7. 20191127 Spring Boot官方文档学习(6-8)

    6.部署Spring Boot应用程序 在部署应用程序时,Spring Boot的灵活打包选项提供了很多选择.您可以将Spring Boot应用程序部署到各种云平台,容器映像(例如Docker)或虚拟 ...

  8. django 的 三个 时间 字段

  9. adb 连接 mumu 模拟器

    [win版]adb connect 127.0.0.1:7555adb shell [mac版] adb kill-server && adb server && ad ...

  10. 关于android工具链

    1 android sdk platform tools 同android platform交互的工具,包括adb.fastboot和systrace. 2 sdk build tools 用于bui ...