import collections
Python的集合(collections)模块,为很多用其他方法很难实现的场景提供了解决方案。他其实是Python高级特性的一个相关应用。collections在我看来给Python内置的数据类型和方法的基础提供了额外的高性能数据类型,比如基础的字典是不支持顺序的,collections模块的OrderedDict类构建的字典可以支持顺序,能简化Python代码,提高Python代码逼格和效率。
Card = collections.namedtuple('Card', ['rank', 'suit'])
这创建了一个叫做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 rank in self.ranks for suit in self.suits]
def __len__(self):
return len(self._cards)
def __getitem__(self, position):
return self._cards[position]
之前创造的Card类表示的是单张纸牌,那么现在创造的Frenchdeck就是表示的一摞纸牌。ranks创建了一个列表['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'],suits也创建了一个列表['spades', 'diamonds', 'clubs', 'hearts'],split()的作用是拆分字符串。之后迭代生成52张card,也就是self._cards。得益于_len_和_getitem_我们可以知道实例的长度以及随机抽取一张卡牌。
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]
deck = FrenchDeck()
for card in sorted(deck, key=spades_high):
print(card)
为了让扑克牌大小有顺序,我们定义了不同suit的大小。定义了一个sapdes_high函数,返回了输入card所对应的值,这个值=面值的大小*4+card的suit。进而进行一个升序排列。
全代码如下:
import collections
import random
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 rank in self.ranks for suit in self.suits]
def __len__(self):
return len(self._cards)
def __getitem__(self, position):
return self._cards[position]
suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0)
print(len(suit_values))
def spades_high(card):
rank_value = FrenchDeck.ranks.index(card.rank)
return rank_value * len(suit_values) + suit_values[card.suit]
deck = FrenchDeck()
for card in sorted(deck, key=spades_high):
print(card)
我们要注意的是,在这个Frenchdeck类里的_len_和_getitem_属于我们自定义的特殊方法,python本身并不存在他存在的是len()和list[]。对于我们菜鸟来说,调用特殊方法要比调用内置函数慢的多,内置函数类似于抄近路。在Python中有许多的特殊方法,但是他是隐式调用的,例如用for对x进行迭代,实际上是背后用iter(x),再往背后看是用x._iter_()。所以我们如果需要调用特殊方法真不如直接调用相应内置函数,又快又准(目前我这么认为)。
还有特殊方法可以让咱们响应各种运算符,例如二维向量:
import math
# Python高效编程
class Vector(object):
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
x = self.x + other.x
y = self.y + other.y
return Vector(x, y)
def __sub__(self, other):
x = self.x - other.x
y = self.y - other.y
return Vector(x, y)
def __abs__(self):
return math.sqrt(self.x ** 2 + self.y ** 2)
def __bool__(self):
return bool(self.x or self.y)
def __mul__(self, times):
return Vector(self.x * times, self.y * times)
def __repr__(self):
return 'Vector({}, {})'.format(self.x, self.y)
创建一个二维向量类,可以让他们之间实现加减取模数乘等运算。
v1 = Vector(3, 4)
v2 = Vector(4, 5)
print(v1+v2)
print(v1-v2)
print(abs(v1))
print(v1*3)
这些6个特殊方法在类的内部,不能直接调用,通常被Python解释器调用。_repr_是为了获取对象的字符串表现形式,如果不定义,结果如下:
print(v1)
>> <__main__.Vector object at 0x000001E3099AEFD0>
_bool_方法,如果向量模为0,则返回false,若不为0则返回true。
我们如果用Python内置类型的实例时候用内置函数例如len(),那么运行速度很快,但是如果是自定义对象,用特殊方法_len_既满足了速度的要求,又满足了语言的一致性