流畅的Python (Fluent Python) —— 第一部分
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) —— 第一部分的更多相关文章
- 流畅的Python (Fluent Python) —— 第二部分01
2.1 内置序列类型概览 Python 标准库用 C 实现了丰富的序列类型,列举如下. 容器序列 list. tuple 和 collections.deque 这些序列能存放不同类型的数据. 扁平序 ...
- 流畅的Python (Fluent Python) —— 前言
本书重点: 这本书并不是一本完备的 Python 使用手册,而是会强调 Python 作为编程语言独有的特性,这些特性或者是只有 Python 才具备的,或者是在其他大众语言里很少见的. Python ...
- 从Python小白到第一个小游戏发布
1.安装必要的环境(附图两张) 直接下载安装程序,本人win10系统,根据电脑系统下载并安装对应的python.exe,安装路径可以选择D盘的,具体安装细节这里就不说了,不知道的可以留言或者找度娘 2 ...
- 「Fluent Python」今年最佳技术书籍
Fluent Python 读书手记 Python数据模型:特殊方法用来给整个语言模型特殊使用,一致性体现.如:__len__, __getitem__ AOP: zope.inteface 列表推导 ...
- Python深入学习之《Fluent Python》 Part 1
Python深入学习之<Fluent Python> Part 1 从上个周末开始看这本<流畅的蟒蛇>,技术是慢慢积累的,Python也是慢慢才能写得优雅(pythonic)的 ...
- Fluent Python: Classmethod vs Staticmethod
Fluent Python一书9.4节比较了 Classmethod 和 Staticmethod 两个装饰器的区别: 给出的结论是一个非常有用(Classmethod), 一个不太有用(Static ...
- Fluent Python: Mutable Types as Parameter Defaults: Bad Idea
在Fluent Python一书第八章有一个示例,未看书以先很难理解这个示例运行的结果,我们先看结果,然后再分析问题原因: 定义了如下Bus类: class Bus: def __init__(sel ...
- 跟着老男孩教育学Python开发【第一篇】:初识Python
Python简介 Python前世今生 Python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解 ...
- Python开发【第一篇】:初识Python
初识python 一.python简介 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解 ...
随机推荐
- [BZOJ4456][ZJOI2016]旅行者:分治+最短路
分析 类似于点分治的思想,只统计经过分割线的最短路,然后把地图一分为二. 代码 #include <bits/stdc++.h> #define rin(i,a,b) for(regist ...
- Spring Boot教程(一)在springboot中用redis实现消息队列
环境依赖 创建一个新的springboot工程,在其pom文件,加入spring-boot-starter-data-redis依赖: <dependency> <groupId&g ...
- C++二维数组(指针)做参数
一.问题描述 使用C++编程过程中经常需要使用到二维数组,然而初级程序员在使用过程中经常会出错使程序崩溃.下面就二维指针的定义,初始化,以及二维指针做参数给出简单介绍. 1.二维数组的定义与初始化 在 ...
- 函数式接口和Lambda表达式
函数式接口(一般标有@FunctionalInterface)就是只定义一个抽象方法的接口. 一个接口,如果满足函数式接口的定义,那么即使不标注为 @FunctionalInterface, 编译器依 ...
- 关于option标签的selected属性
当item的dict_id和custSource一样,那么当前的item的name就被选中并显示在页面 如果直接写selected="selected",就等于直接回显这个集合中最 ...
- 20175308 2018-2019-2 实验四 《Android开发基础》实验报告
20175308 2018-2019-2 实验四 <Android开发基础>实验报告 实验要求 参考 Android开发简易教程 完成云班课中的检查点,也可以先完成实验报告,直接提交.注意 ...
- 关于spotlight_on_oracle的配置及操作
Spotlight是一个强有力的Oracle数据库实时性能诊断工具,提供了一个直观的.可视化的数据库活动展现.Spotlight可视化展现性能瓶颈,一旦某个指标超出可接受的阀值的话.而且,通过下钻功能 ...
- 【零售小程序】—— webview嵌套web端项目(原生开发支付功能)
index → index.wxml 套webwiew // url 活动url bindmessage 接收信息 <web-view src='{{url}}' bindmessage='m ...
- C# 图片文件文本string格式 传输问题
string file = @"E:\test.png"; byte[] bytes = File.ReadAllBytes(file); // 主要代码 string datas ...
- vue 请求完接口后执行方法
getLunbo: function() { var that = this; that.lunbo = []; // api.showProgress({ // title: '加载中' // }) ...