__len__和__getitem__

import collections
import random

Card = collections.namedtuple("Card", ["rank", "suit"])


class FrenchDeck:
    ranks = ["A"] + [str(n) for n in range(2, 11)] + list("JQK")
    suits = "红桃 方块 黑桃 梅花".split()

    def __init__(self):
        self._cards = [Card(rank=rank, suit=suit) for suit in self.suits for rank in self.ranks]

    def __len__(self):
        return len(self._cards)

    def __getitem__(self, item):
        # 此方法对应了[]获取属性
        # 因为把[]操作交给了一个列表,所以支持切片,以及实现了random.choice,以及实现了迭代
        return self._cards[item]


# 通过实现特殊方法来利用Python数据模型有两个显而易见的好处
# 1.作为你的类的用户,他们不必去记住标准操作的各式名称.
# 2.可以更加方便的利用Python的标准库


deck = FrenchDeck()
print(deck[:3])  # 查看前三张牌
print(deck[::13])  # 只看牌面是A的牌
print(random.choice(deck))

# 仅仅实现了__getitem__方法,就变成可迭代的了
for card in deck:
    print(card)
# 反向迭代也没有问题
for card in reversed(deck):
    print(card)

# 迭代通常是隐式的,譬如说一个集合类型没有实现__contains__方法,那么in运算符就会按顺序做一次迭代搜索
print(Card(rank="A", suit="黑桃") in deck)
print(Card(rank="B", suit="黑桃") in deck)

# 纸牌排序  我们按照A最小K最大来,先按牌面排序然后按花色排序. 黑桃最大,梅花最小.
suit_values = dict(黑桃=3, 红桃=2, 方块=1, 梅花=0)  # 构建一个花色权重字典{'黑桃': 3, '红桃': 2, '方块': 1, '梅花': 0}


def spades_high(card_: Card):
    rank_value = FrenchDeck.ranks.index(card_.rank)  # 0-12
    return rank_value * len(suit_values) + suit_values[card_.suit]


for card in sorted(deck, key=spades_high):
    print(card)

# 这里留下一个思考,如何在不破坏类的封装性的基础上,实现洗牌.
原文地址:https://www.cnblogs.com/zyyhxbs/p/13131901.html