图说Python继承

409 阅读3分钟

欢迎订阅公众号 数据分析那些事儿

继承定义

继承性是指,在某种情况下,一个类会有“子类”。子类比原本的类(称为父类)要更加具体化。例如,“狗”这个类可能会有它的子类“牧羊犬”和“吉娃娃犬”。在这种情况下,“莱丝”可能就是牧羊犬的一个实例。子类会继承父类的属性和行为,并且也可包含它们自己的。我们假设“狗”这个类有一个方法(行为)叫做“吠叫()”和一个属性叫做“毛皮颜色”。它的子类(前例中的牧羊犬和吉娃娃犬)会继承这些成员。

总而言之,就是在此前的基础上进行修改,从而定义一个新类的能力。

扑克牌类

以扑克牌为例,一副牌有52张牌,有4种花色(suit)和13个等级(rank)。 4种花色是黑桃(Spades),红心(Hearts),方块(Diamonds),梅花(Clubs),13个等级是A、2、3、4、5、6、7、8、9、10、J、Q、K。

定义一个扑克类,代表一张标准的扑克牌。

class Porker:
    """代表一张标准的扑克牌"""
    def __init__(self, suit=0, rank=2):
        self.suit = suit
        self.rank = rank

类属性

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

    #在Porker类内部
    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' % (Porker.rank_names[self.rank],
                             Porker.suit_names[self.suit])
                             

suit_names 和 rank_names是定义在类内部但在方法之外,被称为类属性,因为他们是被关联到 Porker 类对象上的。 suit 和 rank被关联到了特定的实例,被称为实例属性

创建打印扑克牌

>>> pk = Porker(3,13)
>>> print(pk)
King of Spades
>>>

查看一下Porker和pk的类型

>>> type(Porker)
<class 'type'>
>>> type(pk)
<class '__main__.Porker'>

类属性在内存空间中是如何组织的?下面我们用对象图简释一下

Porker是一个类,它的类型是type;pk是Porker的实例,它的类型是Porker。

一副牌类

class Deck:
  
    def __init__(self):
        """初始化52张牌"""

        self.cards = []
        for suit in range(4):
            for rank in range(1, 14):
                pkr = Porker(suit, rank)
                self.cards.append(pkr)

   def __str__(self):

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

        self.cards.append(card)

    def pop_card(self, i=-1):

        return self.cards.pop(i)

打印一副牌

>>> deck = Deck()
>>> print(deck)
Ace of Clubs
2 of Clubs
3 of Clubs
4 of Clubs
......
8 of Spades
9 of Spades
10 of Spades
Jack of Spades
Queen of Spades
King of Spades

继承

继承指的是在现有类的基础下进行修改,从而定义新类的能力。例如,假设我们想定义一个类来代表手牌(hand),即玩家目前手里有的牌。类之间有相似之处,但也存在不同,这时就可以用上继承了。你只需要在定义新类时,将现有类的名称放在括号里,即可继承现有类。

class Hand(Deck):
    """代表玩家的一手牌"""
    
    def __init__(self, label=''):
        self.cards = []
        self.label = label

Hand 的 init 方法会覆盖从 Deck 类继承来的同名方法。

当你创建一个 Hand 时,Python 会调用这个 init 方法,而不是 Deck 中的同名方法。其它方法是从 Deck 继承来的,所以我们可以使用pop_card 和 add_card来发牌

>>> hand = Hand('new hand of porker')
>>> hand.cards
[]
>>> hand.label
'new hand of porker'

>>> deck = Deck()
>>> card = deck.pop_card()
>>> hand.add_card(card)
>>> print(hand)
King of Spades

总结

一个类中的对象可以包含对另外一个类的对象的引用。例如,每一个矩形包含对点的引用,每一个 Deck 包含对许多 Porker 的引用。这种关系被称为组合( HAS-A ),可以类似这样描述:“一个矩形有一个(has a)点”。

一个类可能继承自另外一个类。这种关系被称为继承(IS-A),可以类似这样描述:“Hand is a kind of Deck”。

参考《Think Python 2e》