《流畅的python》 第一章 示例1-1
特殊方法
python解释器碰到特殊的句法时,会使用特殊方法去激活一些基本的对象操作,这些特殊方法的名字以以两个下划线开头,以两个下划线结尾的方法:__init__
,__len__
,__getitem__
。
这些特殊方法名能让自己的对象实现和支持以下的语言架构,并与之交互:
- 迭代
- 集合类
- 属性访问
- 运算符重载
- 函数和方法的调用
- 对象的创建和销毁
- 字符串表示形式和格式化
- 管理上下文(即with块)
示例1-1
import collections
from random import choice
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, item):
return self._cards[item]
def choice(self):
return choice(self._cards)
首先用**collections.namedtuple
** 构建了一个简单的类来表示一张纸牌。自python2.6,namedtuple
就加入到python,用以构建只有少数属性但是没有方法的对象。
beer_card = Card('7', 'diamonds')
print(beer_card)
输出:
Card(rank=‘7’, suit=‘diamonds’)
可以使用len()来参考一叠牌有多少张,抽取特定的一张纸牌(比如第一张、最后一张)
deck = FrenchDeck()
print(len(deck))
print(deck[0])
print(deck[-1])
print('------------------------------------')
输出:
52
Card(rank=‘2’, suit=‘spades’)
Card(rank=‘A’, suit=‘hearts’)
Python已经内置了从一个序列中随机选出一个元素的函数random.choice
print(deck.choice())
print(deck.choice())
print(deck.choice())
输出:
Card(rank=‘8’, suit=‘hearts’)
Card(rank=‘6’, suit=‘clubs’)
Card(rank=‘10’, suit=‘clubs’)
切片操作
__getitem__
把[]
操作交给了self._cards
列表,所以deck类自动支持切片操作。
print(deck[:3])
print(deck[12::13])
输出:
[Card(rank=‘2’, suit=‘spades’), Card(rank=‘3’, suit=‘spades’), Card(rank=‘4’, suit=‘spades’)]
[Card(rank=‘A’, suit=‘spades’), Card(rank=‘A’, suit=‘diamonds’), Card(rank=‘A’, suit=‘clubs’), Card(rank=‘A’, suit=‘hearts’)]
迭代
for card in deck:
print(card)
反向迭代:
for card in reversed(deck):
print(card)
隐式迭代:in运算符会按顺序做一次迭代搜索
print(Card('Q', 'hearts') in deck)
输出:
True
排序
suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0)
def spades_high(self):
rank_value = FrenchDeck.ranks.index(self.rank)
return rank_value * len(suit_values) + suit_values[self.suit]
for card in sorted(deck, key=spades_high):
print(card)
输出:
Card(rank=‘2’, suit=‘clubs’)
Card(rank=‘2’, suit=‘diamonds’)
Card(rank=‘2’, suit=‘hearts’)
Card(rank=‘2’, suit=‘spades’)
Card(rank=‘3’, suit=‘clubs’)
。。。。。。。。。。。。。
如何使用特殊方法
特殊方法的存在是为了被python解释器调用的,你自己并不需要调用它们。
在执行len(deck)
的时候,deck是一个自定义类的对象,python会自己去调用由你实现的__len__
方法。
如果是python内置的类型,比如列表(list)、字符串(str)、字节序列(bytearray)等,那么CPython会抄个近路,__len__
实际上会直接返回PyVarObject里的ob_size属性。直接读取这个值比调用一个方法要快很多。
很多时候,特殊方法的调用是隐式的,比如 for i in x:
这个语句,背后其实用的是iter(x)
,而这个函数的背后则是x.__iter__()
(前提是这个方法在x中被实现了)。
通常你的代码无需直接使用特殊方法。
通过内置的函数(len、iter、str等)来使用特殊方法是最好的选择。
不要自己想当然的随意添加特殊方法。