实现 len 和 []
我们看到 『Python』可以用 len() 或者arr[0],它们是怎么实现的,其实就是两个魔法方法__len__和__getitem__,用扑克牌的例子学习一下,我们知道知道扑克牌有花色(黑桃,红桃,方块,梅花),有大小(2 -10 JQKA),如何创造一套牌的对象呢?
首先学习 collections的 namedtuple,这是一个快速创建对象,有少数属性但是没有方法的,进一步学习可以查相关文档。这个非常适合作我们一张牌的对象。所以创建一套牌对象代码如下:
from collections import namedtuple
Card = 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]
上面是一套牌对象,有魔法方法__len__,__getitem__
我们测试一下代码
if __name__ == '__main__':
import random
deck = FrenchDeck()
# 求长度
print("长度", len(deck))
# 索引
print("索引0", deck[0])
print("索引-1", deck[-1])
# 切片
print("切片", deck[1:4:2])
# 迭代
for card in deck:
print("迭代", card)
# 包含
print("包含", Card("Q", "hearts") in deck)
# 随机选择
print("随机选择", random.choice(deck))
# 排序 我们认为数字2最小 A最大 花色 黑桃>红桃>方块>梅花
def sort_help(card):
suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0)
rank_val = deck.ranks.index(card.rank)
return rank_val * 4 + suit_values[card.suit]
for card in sorted(deck, key=sort_help):
print("排序", card)
我们可以把对象像数组一样操作,可以求长度,索引,迭代,包含,排序等。。。
模拟数值类型
我们用向量来举例子,要实现一下需求向量加法 Vector(2, 4) + Vector(2, 1) = Vector(4, 6)
向量乘法Vector(3, 4) * 3 = Vector(9, 12)
向量的模 |Vector(3, 4)| = 5
上面Vector类的实现要用到 __repr__, __abs__, __add__, __mul__代码如下
from math import hypot
class Vector:
def __init__(self, x: int = 0, y: int = 0):
self.x = x
self.y = y
def __repr__(self):
return "Vector({}, {})".format(self.x, self.y)
# 求模
def __abs__(self) -> float:
return hypot(self.x, self.y)
# 求bool
def __bool__(self) -> bool:
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, other):
return Vector(self.x * other, self.y * other)我们
我们测试代码
if __name__ == '__main__':
x1 = Vector(3, 4)
x2 = Vector(6, 8)
# __repr__ 和 __str__ 的区别
# __str__ 只有 str 或者 print被使用
# 如果只想实现一个 __repr__是更好的选择,因为如果一个对象
# 没有 __str__ 函数,而 Python 又需要调用它的时候,解释器会用 __repr__ 作为替代。
print(x1)
# 求模
print(abs(x1))
# 加
print(x1 + x2)
# 乘
print(x1 * 3)
# bool
print(bool(x1))
附上特殊方法一览表:跟运算符无关的特殊方法
运算符相关的
小结:我们自定义数据可以表现和内置类型一样,通过魔法方法实现。
__repr__方便我们调试和记录日志,__str__来给终端用户看。
记录学习 《流畅的python》第一天,一定要坚持下去!