数据模型
python的纸牌风格
通过实现特殊方法来利用 Python 数据模型的两个好处:
- 作为你的类的用户,他们不必去记住标准操作的各式名称
- 可以更加方便地利用Python的标准库,比如random.choice 函数,从而不用重新发明轮子
import collections
# namedtuple 用以构建只有少数属性但是没有方法的对象
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,position):
return self._cards[position]
# namedtuple,我们可以很轻松地得到一个纸牌对象
beer_card = Card('7','diamonds')
beer_card
Card(rank='7', suit='diamonds')
# __getitem__ 提供的方法
deck = FrenchDeck()
print(len(deck))
# deck[0]是由 __getitem__ 方法提供
# 如果去掉会报错
print(deck[0])
print(deck[-1])
52
Card(rank='2', suit='spades')
Card(rank='A', suit='hearts')
# 随机抽取一张纸牌 random.choice
from random import choice
choice(deck)
Card(rank='5', suit='hearts')
#切片操作
print(deck[:3])
#先抽出索引是 12 的那张牌,然后每隔 13 张牌拿 1 张
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)
# 一个集合类型没有实现 __contains__ 方法,那么 in 运算符就会按顺序做一次迭代搜索
print(Card('Q','hearts') in deck)
# 排序
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]
for card in sorted(deck,key=spades_high):
print(card)
特殊方法的使用
特殊方法,即便其他程序要使用这个类的这些方法,也不会直接调用它们。如下面的实例。
repr 字符串表示形式
repr 就是通过 repr 这个特殊方法来得到一个对象的字符串表示形式的。
例如下面的例子,会返回Vector(1,2)
- 在 repr 的实现中,用到了 %r 来获取对象各个属性的标准字符串表示形式
- repr 所返回的字符串应该准确、无歧义,并且尽可能表达出如何用代码创建出这个被打印的对象。
- repr 和 str 的区别在于,后者是在 str() 函数被使用,,repr 是更好的选择
算术运算符
通过__add__ 和__mul__,示例为向量类带来了 + 和 * 这两个算术运算符。
算术运算符的基本原则就是不改变操作对象,而是产出一个新的值。
自定义的布尔值
上任何对象都可以用于需要布尔值的上下文中
我们自己定义的类的实例总被认为是真的,除非这个类对 bool 或者 __
len__ 函数有自己的实现。bool(x) 的背后是调用 x.bool() 的结果;如果不存在 bool 方法,那么 bool(x) 会尝试调用 x.len()。若返回 0,则 bool 会返回 False;否则返回 True。
特殊方法一览
from math import hypot
class Vector:
def __init__(self,x=0,y=0):
self.x = x
self.y = y
def __repr__(self):
return 'Vector(%r,%r)'%(self.x,self.y)
def __abs__(self):
return hypot(self.x,self.y)
def __bool__(self):
return bool(abs(self))
def __add__(self,other):
x = self.x + other.x
y = self.y + other.y
return Vector(x,y)
def __mul__(self,scalar):
return Vector(self.x*scalar,self.y * scalar)
v1 = Vector(2,4)
v2 = Vector(1,2)
print(v1+v2)
print(abs(v1))
print(v2 * 2)
Vector(3,6)
4.47213595499958
Vector(2,4)
小结
- 通过实现特殊方法,自定义数据类型可以表现得跟内置类型一样
- 通过 repr 和__str__ 来满足字符串表示形式。前者方便我们调试和记录日志,后者则是给终端用户看的
- 对序列数据类型的模拟是特殊方法用得最多的地方
- 通过运算符重载这一模式提供了丰富的数值类型
分享关于人工智能,机器学习,深度学习以及计算机视觉的好文章,同时自己对于这个领域学习心得笔记。想要一起深入学习人工智能的小伙伴一起结伴学习吧!扫码上车!