简单的Python继承

14
class Animal(object):
    def __init__(self, nlegs=4):
        print '__init__ Animal'
        self.nlegs = nlegs

class Cat(Animal):
    def __init__(self, talk='meow'):
        print '__init__ Cat'
        self.talk = talk

class Dog(Animal):
    def __init__(self, talk='woof'):
        print '__init__ Dog'
        self.talk = talk
  1. 为什么我的猫tom = Cat()没有nlegs属性?
  2. 我们是否应该在Cat.__init__中显式调用Animal.__init__(),或者应该使用更高级的方法(比如说super)?
  3. 如果我想要创建一只有5只脚的猫,我需要向Cat.__init__接口添加额外的参数吗?
3个回答

16
为了扩充其他人所说的内容,是的,你需要调用父类的__init__方法。
通常最好使用super。然而,在某些情况下(特别是当你从多个类继承时),它可能会出现大问题。我不会详细介绍,有很多各种讨论它的文章。(此外,一些其他“特殊”函数也存在一些奇怪的地方。例如,你可以执行super(SomeCls, self).__getitem__(5),但super(SomeCls, self)[5]将不起作用。)
作为一个简单的例子,说明为什么这是一个好习惯:你可以让Dog和Cat继承自Mammal(继承自Animal),而不必改变代码中的其他位置,只需改变Dog和Cat继承的类即可。
至于为什么你的tom实例没有tom.nlegs,那是因为你没有调用Animal的__init__方法。
另外请记住,并非所有东西都需要在初始化时设置。对于这个例子,不在__init__方法中设置类似nlegs这样的东西更加合理。相反,直接在类中设置即可,例如:
class Mammal(object):
    nlimbs = 4
    def __init__(self):
        print "I'm a mammal!"

class Cat(Mammal):
    def __init__(self, color="calico"):
        self.color = color
        super(Cat, self).__init__()
        print "I have {0} legs I am {1}".format(self.nlimbs, self.color)

class FiveLeggedCat(Cat):
    nlimbs = 5

如果某些东西在不同的实例中可能会发生变化(例如猫的颜色),或者需要在初始化时完成(例如打开文件),那么它很可能应该在__init__中设置。

否则,如果我们希望该属性对于类的任何实例都是相同的,则直接在类定义中设置会更清晰。

此外,以这种方式设置的属性将可供文档工具(例如内置的help函数)使用,而在初始化时设置的属性则不会。


1
谢谢!那么为什么在这个例子中直接设置在类中更有意义呢?您能否澄清何时最好在初始化时设置属性,何时最好在类定义中设置属性? - wim
@wim - 我添加了一些编辑以澄清一些问题。简而言之,如果它是每个实例都会改变的内容,则设置在__init__构造函数中。否则,使__init__函数更简单,并直接在类定义中设置它。希望能有所帮助! - Joe Kington
我认为并不是每种动物都有四条腿(有些甚至没有腿)。因此,它不应该成为动物类的一部分。 - JBernardo
@JBernardo - 确实,说得好!我将它改成了哺乳动物类。除鲸鱼等特殊情况外(它们仍然有腿,只是变成了退化的内部骨骼),大多数(未受伤/突变的)哺乳动物都有四肢,所以这更加合适。 - Joe Kington

4

使用super

class Cat(Animal):
    def __init__(self, talk='meow', num_legs=4):
        print 'Hay cat'
        self.talk = talk
        super(Cat, self).__init__(num_legs)


tom = Cat()  #tom is a normal cat
bob = Cat('Nyan', 3) #bob is a japanese defective cat

1
是的。如果派生类没有定义__init__方法,则使用父类的__init__方法。您不需要对object做任何操作。 - JBernardo

2

你需要查看关于 super() 的Python文档。例如,你通常应该在你的Cat.__init__()方法中开始(或结束)调用super(Cat, self).__init__(<任何参数>)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接