简体   繁体   English

Python 继承:何时以及为何 __init__

[英]Python inheritance: when and why __init__

I'm a Python newbie, trying to understand the philosophy/logic behind the inheritance methods.我是 Python 新手,试图了解继承方法背后的哲学/逻辑。 Questions ultimately regards why and when one has to use the __init__ method in a subclass.问题最终是关于为什么以及何时必须在子类中使用__init__方法。 Example:例子:

It seems a subclass inheriting from a superclass need not have its own constructor (__init__ ) method.似乎从超类继承的子类不需要有自己的构造函数(__init__ ) 方法。 Below, a dog inherits the attributes (name, age) and methods (makenoise) of a mammal.下面,一只狗继承了哺乳动物的属性(名字、年龄)和方法(makenoise)。 You can even add a method ( do_a_trick ) Everything works as it ``should", it seems.您甚至可以添加一个方法 ( do_a_trick ) 看起来一切都“应该”地工作。

However, if I wanted to add a new attribute in the subclass as I attempt to do in the Cats class, I get an error saying "self" is not defined.但是,如果我想像我在 Cats 类中尝试做的那样在子类中添加一个新属性,我会收到一条错误消息,指出“self”未定义。 Yet I used "self" in the definition of the dog class.然而我在 dog 类的定义中使用了“self”。 What's the nature of the difference?区别的本质是什么? It seems to define Cats as I wish I need to use __init__(self,name) and super()__init__(name) .它似乎定义了猫,因为我希望我需要使用__init__(self,name)super()__init__(name) Why the difference?为什么不同?

class Mammals(object):

  def __init__(self,name):
    self.name = name
    print("I am a new-born "+ self.name)  
    self.age = 0

  def makenoise(self):
    print(self.name + " says Hello")

class Dogs(Mammals):

  def do_a_trick(self):
    print(self.name + " can roll over")

class Cats(Mammals):

 self.furry = "True"  #results in error `self' is not defined


mymammal = Mammals("zebra") #output "I am a new-born zebra"
mymammal.makenoise()  #output "zebra says hello"
print(mymmmal.age)    #output 0

mydog = Dogs("family pet") #output "I am a new-born family pet"
mydog.makenoise()  #output "family pet says hello"
print(mydog.age)  # output 0
mydog.do_a_trick() #output "family pet can roll over"

Explicit is better than implicit.显式优于隐式。

However, you can do below:但是,您可以执行以下操作:

class Dogs(Mammals):
    def __init__(self):
        #add new attribute
        self.someattribute = 'value'
        Mammals.__init__(self)

or要么

class Dogs(Mammals):
    def __init__(self):
        #add new attribute
        self.someattribute = 'value'
        super(Mammals, self).__init__()

if I wanted to add a new attribute in the subclass as I attempt to do in the Cats class, I get an error saying "self" is not defined.如果我想像我在 Cats 类中尝试做的那样在子类中添加一个新属性,我会收到一条错误消息,指出“self”未定义。 Yet I used "self" in the definition of the dog class.然而我在 dog 类的定义中使用了“self”。

In your superclass, Mammal, you have an __init__ function, which takes an argument that you've chosen* to call self .在你的超类 Mammal 中,你有一个__init__函数,它接受一个你选择*调用self的参数。 This argument is in scope when you're in the body of the __init__ function - it's a local variable like any local variable, and it's not possible to refer to it after its containing function terminates.当您在__init__函数的主体中时,此参数在范围内 - 它是一个像任何局部变量一样的局部变量,并且在包含它的函数终止后无法引用它。 The function defined on the Dog class, do_a_trick , also takes an argument called self , and it is also local to that function.在 Dog 类上定义的函数do_a_trick也接受一个名为self的参数,它也是该函数的本地参数。 What makes these variables special is not their name (you could call them anything you wanted) but the fact that, as the first arguments to instance methods in python, they get a reference to the object on which they're called as their value.使这些变量与众不同的不是它们的名称(您可以随心所欲地称呼它们),而是事实上,作为 python 中实例方法的第一个参数,它们获得了对调用它们的对象的引用作为它们的值。 (Read that last sentence again a few times, it's the key to understanding this, and you probably won't get it the first time.) (把最后一句话再读几遍,这是理解这一点的关键,你可能第一次看不懂。)

Now, in Cat , you have a line of code which is not in a function at all.现在,在Cat中,您有一行代码根本不在函数中。 Nothing is in scope at this point, including self , which is why this fails.此时范围内没有任何内容,包括self ,这就是失败的原因。 If you were to define a function in Cat that took an argument called self , it would be possible to refer to that argument.如果您要在Cat中定义一个带有名为self的参数的函数,则可以引用该参数。 If that argument happened to be the first argument to an instance method on Cat , then it would have the value of the instance of Cat on which it had been called.如果该参数恰好是Cat上实例方法的第一个参数,那么它将具有调用它的Cat实例的值。 Otherwise, it would have whatever got passed to it.否则,它会拥有传递给它的任何东西。

*you have chosen wisely! *你的选择很明智!

Declarations at the top level of a Python class become class attributes. Python 类顶层的声明成为类属性。 If you come from a C++ or Java background, this is similar to declaring a static member variable.如果您来自 C++ 或 Java 背景,这类似于声明静态成员变量。 You cannot assign instance attributes at that level.您不能在该级别分配实例属性。

The variable self usually refers to a specific instance of a class, the one from which the method has been called.变量self通常指类的一个特定实例,即从中调用方法的实例。 When a method call is made using the syntax inst.method() , the first argument to the function is the object inst on which the method was called.当使用语法inst.method()进行方法调用时,函数的第一个参数是调用该方法的对象inst In your case, and usually by convention, that argument is named self within the function body of methods.在您的情况下,通常按照惯例,该参数在方法的函数体内被命名为self You can think of self as only being a valid identifier within method bodies then.你可以认为self只是方法体内的一个有效标识符。 Your assignment self.furry = True does not take place in a method, so self isn't defined there.您的作业self.furry = True不在方法中进行,因此 self 未在此处定义。

You have basically two options for achieving what you want.你基本上有两种选择来实现你想要的。 The first is to properly define furry as an attribute of the cat class:首先是将furry正确定义为 cat 类的属性:

class Cat(Mammals):
    furry = True

    # Rest of Cat implementation ...

or you can set the value of an instance variable furry in the cat constructor:或者您可以在 cat 构造函数中设置实例变量furry的值:

class Cat(Mammals):
    def __init__(self):
        super(Mammals, self).__init__(self)
        self.furry = True

    # Rest of Cat implementation ...

If you're getting into Python I highly recommend to read these two parts of the Python documentation:如果您正在接触 Python,我强烈建议您阅读 Python 文档的这两部分:

Python classes Python 类

Python data model special methods (more advanced) Python数据模型特殊方法(更进阶)

As pointed out in the other answers, the self that you see in the other functions is actually a parameter.正如其他答案所指出的,您在其他函数中看到的self实际上是一个参数。 By Python convention, the first parameter in an instance method is always self .按照 Python 的惯例,实例方法中的第一个参数始终是self

The class Cats inherits the __init__ function from its base class, Mammals . Cats类从其基类Mammals继承了__init__函数。 You can override __init__ , and you can call or not call the base class implementation.你可以覆盖__init__ ,你可以调用或不调用基类实现。

In case the Cats __init__ wants to call the base implementation, but doesn't want to care about the parameters, you can use Python variable arguments.如果Cats __init__想要调用基本实现,但不想关心参数,则可以使用 Python 变量参数。 This is shown in the following code.这在以下代码中显示。

Class declaration:类声明:

class Cats(Mammals):

  def __init__(self, *args):
    super().__init__(*args)
    self.furry = "True"

See, for example, this Stack Overflow question for something about the star notation for variable numbers of arguments: Can a variable number of arguments be passed to a function?例如,请参阅这个 Stack Overflow 问题,了解有关可变数量参数的星号表示法的一些信息: Can a variable number of arguments be passed to a function?

Additional test code:附加测试代码:

cat = Cats("cat")
print(vars(cat))

Output:输出:

I am a new-born cat
{'name': 'cat', 'age': 0, 'furry': 'True'}

You can do something like in Chankey's answer by initiating all the variables in the constructor method ie __init__您可以通过在构造函数方法即__init__中启动所有变量来执行 Chankey 的回答中的操作

However you can also do something like this但是你也可以这样做

class Cats(Mammals):

 furry = "True"

And then接着

cat = Cats("Tom")
cat.furry # Returns "True"

The reason you can't use self outside the functions is because self is used explicitly only for instances of the class.不能在函数外使用self的原因是因为self仅明确用于类的实例。 If you used it outside, it would lead to ambiguity.如果你在外面使用它,它会导致歧义。 If my answer isn't clear please let me know in comments.如果我的回答不清楚,请在评论中告诉我。

The __init__ method runs once on the creation of an instance of a class. __init__方法在创建类实例时运行一次。 So if you want to set an attribute on an instance when it's created, that's where you do it.因此,如果您想在实例创建时为其设置一个属性,就在此处进行。 self is a special keyword that is passed as the first argument to every method, and it refers to the instance itself. self是一个特殊的关键字,作为第一个参数传递给每个方法,它指的是实例本身。 __init__ is no different from other methods in this regard. __init__在这方面与其他方法没有什么不同。

"What's the nature of the difference": you define the method Dog.do_a_trick , and you receive self as an argument to the method as usual. “区别的本质是什么”:您定义方法Dog.do_a_trick ,并且像往常一样接收self作为该方法的参数。 But in Cat you've unintentionally (perhaps subconsciously!) attempted to work on the class scope -- this is how you'd set a class attribute whose value is identical for all cats:但是在Cat中,你无意中(也许是下意识地!)试图在类范围内工作——这就是你如何设置一个类属性,它的值对所有猫都是相同的:

class Cat(object):
    sound = "meow"

It's different so you can have both options available.它是不同的,因此您可以同时使用这两个选项。 Sometimes (not all the time, but once in a while) a class attribute is a useful thing to have.有时(不是所有时候,而是偶尔)拥有一个类属性是很有用的。 All cats have the same sound.所有的猫都有相同的声音。 But much of the time you'll work with instance attributes -- different cats have different names;但是很多时候您将使用实例属性——不同的猫有不同的名字; when you need that, use __init__ .当你需要时,使用__init__

Suppose you have a class named Person which has a method named get_name defined as:假设你有一个名为Person的类,它有一个名为get_name的方法,定义如下:

 class Person():
     def __init__(self, first_name, last_name):
         self.first_name = first_name
         self.last_name = last_name

     def get_name(self):
         return self.first_name + ' ' + self.last_name

And, you create an instance of Person as p1 .并且,您将Person的实例创建为p1 Now when you call the function get_name() with this instance, it will converts internally现在当你用这个实例调用函数get_name()时,它会在内部转换

    Person.get_name(p1)

So, self is the instance itself.所以,self 就是实例本身。

Without self you can write above code as:没有self你可以将上面的代码写成:

 class Person():
     first_name = None
     last_name = None


     def get_name(personobject):
         return personobject.first_name + ' ' + personobject.last_name

What I am trying to say is the name self is a convention only.我想说的是名称self只是一个约定。

And for inheritance, if you would like to have extra attributes in your subclass, you need to initiate your super class first and add your parameter as you wanted.而对于继承,如果你想在你的子类中有额外的属性,你需要先启动你的超类并根据需要添加你的参数。 For example, if you want to create a subclass from Person named Boy with new attribute height , the you can define it as:例如,如果你想从名为BoyPerson创建一个具有新属性height的子类,你可以将其定义为:

class Boy(Person):
     def __init__(self, first_name, last_name, height):
         super(Person, self).__init__(first_name, last_name)
         self.height = height

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM