例子,卡牌继承
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())
类之间有如下几种关系:
- 一个类中的对象可以包含对另外一个类的对象的引用。例如,每一个矩形包含对点的引用,每一个 Deck 包含对许多 Card 的引用。这种关系被称为组合( HAS-A ),可以类似这样描述:“一个矩形有一个(has a)点”。
- 一个类可能继承自另外一个类。这种关系被称为继承(IS-A),可以类似这样描述:“Hand is a kind of Deck”。
- 一个类可能强赖另一个类,因为前者中的对象接受后者中的对象作为参数,或者使用后者中的对象作为计算的一部分。这种关系被称为 依赖 。
带空心三角的箭头表示 IS-A 的关系;这里它表示 Hand 继承自 Deck 。
标准箭头表示 HAS-A 的关系;这里表示 Deck 包含对 Card 对象的引用。
网友评论