python-11 继承

2020-04-13  本文已影响0人  巴巴11

例子,卡牌继承

Card的类定义如下:

class Card:
    """代表一张标准的卡牌"""

    def __init__(self, suit=0, rank=2):
        self.suit = suit
        self.rank = rank
通常,init 方法接受针对每个属性的可选形参。默认的卡牌是梅花 2。

可以使用你需要的花色和等级调用 Card ,创建一个 Card 对象。

queen_of_diamonds = Card(1, 12)


类属性
为了以大家能够轻松看懂的方式来打印卡牌对象,我们需要一个从整数码到对应的等级和花色的映射。 一种直接的方法是使用字符串列表。我们把这些列表赋值到类属性:

# 在Card类内部:

    suit_names = ['Clubs', 'Diamonds', 'Hearts', 'Spades']
    rank_names = [None, 'Ace', '2', '3', '4', '5', '6', '7',
              '8', '9', '10', 'Jack', 'Queen', 'King']

    def __str__(self):
        return '%s of %s' % (Card.rank_names[self.rank],
                             Card.suit_names[self.suit])
像 suit_names 和 rank_names 这样的变量,是定义在类内部但在方法之外, 被称为类属性。因为他们是被关联到 Card 类对象上的。

这个术语将它们同 suit 和 rank 这样的变量区分开来,后者被称为实例属性, 因为他们被关联到了特定的实例。

这两种属性都使用点标记法来访问。例如,在__str__中,self 是一个卡牌对 象,self.rank 是它的等级。 同样的,Card 是一个类对象,Card.rank_names是一个和类关联的字符串列表。

每一张卡牌都有自己的花色和等级, 但是这里只有一份suit_names和rank_names拷贝。

综合来说,表达式Card.rank_names[self.rank]表示“使用 self 对象 中的 rank 属性作为 Card 类的rank_names列表的索引下标,然后获取相应的字符串。”

rank_names的第一个元素是 None ,因为没有卡牌的等级是 0 。 通过使用 None 作为占位符,我们可以很好地将索引 2 映射到字符串'2',等等。 为了避免使用这种小技巧,我们也可以使用一个字典来代替列表。

利用现有的方法,我们可以创建和打印卡牌:
>>> card1 = Card(2, 11)
>>> print(card1)
Jack of Hearts

对于程序员自定义的类型,我们可以通过提供一个叫 __lt__(代表“小于”)的方法,来覆盖内建运算符的行为。

__lt__接受 2 个参数, self 和 other,如果 self 比 other 的值要小则返回 True 。

# 在Card类内部:

    def __lt__(self, other):
        # 判断花色
        if self.suit < other.suit: return True
        if self.suit > other.suit: return False

        # 花色相同...判断等级
        return self.rank < other.rank

可以使用元组比较来使得代码更加简洁:

# 在Card类内部:

    def __lt__(self, other):
        t1 = self.suit, self.rank
        t2 = other.suit, other.rank
        return t1 < t2

一副牌
class Deck:

    def __init__(self):
        self.cards = []
        for suit in range(4):
            for rank in range(1, 14):
                card = Card(suit, rank)
                self.cards.append(card)

打印一副牌
下面是为 Deck 定义的 __str__ 方法:

# Deck类的内部

    def __str__(self):
        res = []
        for card in self.cards:
            res.append(str(card))
        return '\n'.join(res)

用列表的 sort 方法来写一个 Deck 的 sort 方法,给卡牌排序。 sort使用我们定义的__cmp__来决定排序顺序。


继承

class Hand(Deck):
    """Represents a hand of playing cards."""

这个定义表明,Hand 继承自 Deck ;这意味着我们也可以对 Hands 使用 Deck 的pop_card和add_card方法。

当一个新类继承自一个现有类时,现有类被称为 父类 ,新类被称为 子类 。

在此例中,Hand 继承了 Deck 的__init__方法,但是它并没有满足我们的要求:init 方法应该为 Hand 初始化一个空的 cards 列表,而不是往手牌里添加 52 张新牌。

如果我们提供一个 Hand 的 init 方法,它会覆盖从 Deck 类继承来的同名方法。

# Hand 类的内部

    def __init__(self, label=''):
        self.cards = []
        self.label = label
当你创建一个 Hand 时,Python 会调用这个 init 方法,而不是 Deck 中的同名方法。

>>> hand = Hand('new hand')
>>> hand.cards
[]
>>> hand.label
'new hand'
其它方法是从 Deck 继承来的,所以我们可以使用pop_card 和 add_card来发牌:

>>> deck = Deck()
>>> card = deck.pop_card()
>>> hand.add_card(card)
>>> print(hand)
King of Spades
很自然地,下一步就是把这些代码封装进一个叫move_cards的方法:

# Deck类的内部

    def move_cards(self, hand, num):
        for i in range(num):
            hand.add_card(self.pop_card())

类之间有如下几种关系:

带空心三角的箭头表示 IS-A 的关系;这里它表示 Hand 继承自 Deck 。

标准箭头表示 HAS-A 的关系;这里表示 Deck 包含对 Card 对象的引用。

上一篇 下一篇

猜你喜欢

热点阅读