简体   繁体   English

为什么我们在 Python 类中使用 __init__?

[英]Why do we use __init__ in Python classes?

I am having trouble understanding the Initialization of classes.我无法理解类的初始化。

What's the point of them and how do we know what to include in them?它们有什么意义,我们如何知道它们包含什么? Does writing in classes require a different type of thinking versus creating functions (I figured I could just create functions and then just wrap them in a class so I can re-use them. Will that work?)与创建函数相比,在类中编写是否需要不同类型的思维(我想我可以只创建函数,然后将它们包装在 class 中,这样我就可以重新使用它们。这行得通吗?)

Here's an example:这是一个例子:

class crawler:
  # Initialize the crawler with the name of database
  def __init__(self,dbname):
    self.con=sqlite.connect(dbname)

  def __del__(self):
    self.con.close()

  def dbcommit(self):
    self.con.commit()

Or another code sample:或者另一个代码示例:

class bicluster:
  def __init__(self,vec,left=None,right=None,distance=0.0,id=None):
    self.left=left
    self.right=right
    self.vec=vec
    self.id=id
    self.distance=distance

There are so many classes with __init__ I come across when trying to read other people's code, but I don't understand the logic in creating them.我在尝试阅读其他人的代码时遇到了很多带有__init__的类,但我不理解创建它们的逻辑。

By what you wrote, you are missing a critical piece of understanding: the difference between a class and an object.根据你写的内容,你错过了一个关键的理解:类和对象之间的区别。 __init__ doesn't initialize a class, it initializes an instance of a class or an object. __init__不初始化类,它初始化类或对象的实例。 Each dog has colour, but dogs as a class don't.每条狗都有颜色,但狗作为一个类没有。 Each dog has four or fewer feet, but the class of dogs doesn't.每只狗都有四只或更少的脚,但狗类没有。 The class is a concept of an object.类是对象的概念。 When you see Fido and Spot, you recognise their similarity, their doghood.当您看到 Fido 和 Spot 时,您会认识到他们的相似之处,他们的狗血统。 That's the class.这就是班级。

When you say当你说

class Dog:
    def __init__(self, legs, colour):
        self.legs = legs
        self.colour = colour

fido = Dog(4, "brown")
spot = Dog(3, "mostly yellow")

You're saying, Fido is a brown dog with 4 legs while Spot is a bit of a cripple and is mostly yellow.你是说,Fido 是一只有 4 条腿的棕色狗,而 Spot 有点跛子,大部分是黄色的。 The __init__ function is called a constructor, or initializer, and is automatically called when you create a new instance of a class. __init__函数称为构造函数或初始化程序,并在您创建类的新实例时自动调用。 Within that function, the newly created object is assigned to the parameter self .在该函数中,新创建的对象被分配给参数self The notation self.legs is an attribute called legs of the object in the variable self .符号self.legs是变量self称为对象的legs的属性。 Attributes are kind of like variables, but they describe the state of an object, or particular actions (functions) available to the object.属性有点像变量,但它们描述对象的状态,或对象可用的特定操作(函数)。

However, notice that you don't set colour for the doghood itself - it's an abstract concept.但是,请注意您没有为 doghood 本身设置colour - 这是一个抽象的概念。 There are attributes that make sense on classes.有些属性对类有意义。 For instance, population_size is one such - it doesn't make sense to count the Fido because Fido is always one.例如, population_size就是这样——计算 Fido 没有意义,因为 Fido 总是一。 It does make sense to count dogs.数狗确实有意义。 Let us say there're 200 million dogs in the world.假设世界上有 2 亿只狗。 It's the property of the Dog class.它是 Dog 类的属性。 Fido has nothing to do with the number 200 million, nor does Spot. Fido 与 2 亿这个数字无关,Spot 也没有。 It's called a "class attribute", as opposed to "instance attributes" that are colour or legs above.它被称为“类属性”,而不是上面的colourlegs “实例属性”。

Now, to something less canine and more programming-related.现在,少一些犬类和更多与编程相关的东西。 As I write below, class to add things is not sensible - what is it a class of?正如我在下面写的,添加东西的类是不明智的 - 它是什么类? Classes in Python make up of collections of different data, that behave similarly. Python 中的类由不同数据的集合组成,它们的行为相似。 Class of dogs consists of Fido and Spot and 199999999998 other animals similar to them, all of them peeing on lampposts.狗类包括 Fido 和 Spot 以及 199999999998 其他与它们相似的动物,它们都在灯柱上撒尿。 What does the class for adding things consist of?添加东西的类由什么组成? By what data inherent to them do they differ?它们的不同之处在于它们固有的哪些数据? And what actions do they share?他们分享什么行动?

However, numbers... those are more interesting subjects.然而,数字……那些是更有趣的主题。 Say, Integers.说,整数。 There's a lot of them, a lot more than dogs.他们有很多,比狗多得多。 I know that Python already has integers, but let's play dumb and "implement" them again (by cheating and using Python's integers).我知道 Python 已经有了整数,但让我们装傻再“实现”它们(通过欺骗和使用 Python 的整数)。

So, Integers are a class.所以,整数是一个类。 They have some data (value), and some behaviours ("add me to this other number").他们有一些数据(值)和一些行为(“将我添加到另一个数字”)。 Let's show this:让我们展示一下:

class MyInteger:
    def __init__(self, newvalue):
        # imagine self as an index card.
        # under the heading of "value", we will write
        # the contents of the variable newvalue.
        self.value = newvalue
    def add(self, other):
        # when an integer wants to add itself to another integer,
        # we'll take their values and add them together,
        # then make a new integer with the result value.
        return MyInteger(self.value + other.value)

three = MyInteger(3)
# three now contains an object of class MyInteger
# three.value is now 3
five = MyInteger(5)
# five now contains an object of class MyInteger
# five.value is now 5
eight = three.add(five)
# here, we invoked the three's behaviour of adding another integer
# now, eight.value is three.value + five.value = 3 + 5 = 8
print eight.value
# ==> 8

This is a bit fragile (we're assuming other will be a MyInteger), but we'll ignore now.这有点脆弱(我们假设other将是 MyInteger),但我们现在将忽略。 In real code, we wouldn't;在实际代码中,我们不会; we'd test it to make sure, and maybe even coerce it ("you're not an integer? by golly, you have 10 nanoseconds to become one! 9... 8....")我们会测试它以确保,甚至可能会强制它(“你不是一个整数?天哪,你有 10 纳秒变成一个!9 ... 8 ....”)

We could even define fractions.我们甚至可以定义分数。 Fractions also know how to add themselves.分数也知道如何添加自己。

class MyFraction:
    def __init__(self, newnumerator, newdenominator):
        self.numerator = newnumerator
        self.denominator = newdenominator
        # because every fraction is described by these two things
    def add(self, other):
        newdenominator = self.denominator * other.denominator
        newnumerator = self.numerator * other.denominator + self.denominator * other.numerator
        return MyFraction(newnumerator, newdenominator)

There's even more fractions than integers (not really, but computers don't know that).分数甚至比整数还多(不是真的,但计算机不知道)。 Let's make two:让我们做两个:

half = MyFraction(1, 2)
third = MyFraction(1, 3)
five_sixths = half.add(third)
print five_sixths.numerator
# ==> 5
print five_sixths.denominator
# ==> 6

You're not actually declaring anything here.你实际上并没有在这里声明任何东西。 Attributes are like a new kind of variable.属性就像一种新的变量。 Normal variables only have one value.正常变量只有一个值。 Let us say you write colour = "grey" .假设您写colour = "grey" You can't have another variable named colour that is "fuchsia" - not in the same place in the code.你不能有另一个名为colour变量是"fuchsia" ——不在代码中的同一个地方。

Arrays solve that to a degree.数组在一定程度上解决了这个问题。 If you say colour = ["grey", "fuchsia"] , you have stacked two colours into the variable, but you distinguish them by their position (0, or 1, in this case).如果您说colour = ["grey", "fuchsia"] ,则您已将两种颜色堆叠到变量中,但您可以通过它们的位置(在本例中为 0 或 1)来区分它们。

Attributes are variables that are bound to an object.属性是绑定到对象的变量。 Like with arrays, we can have plenty colour variables, on different dogs .与数组一样,我们可以在不同的狗上拥有大量colour变量。 So, fido.colour is one variable, but spot.colour is another.所以, fido.colour是一个变量,但spot.colour是另一个变量。 The first one is bound to the object within the variable fido ;第一个绑定到变量fido的对象; the second, spot .第二, spot Now, when you call Dog(4, "brown") , or three.add(five) , there will always be an invisible parameter, which will be assigned to the dangling extra one at the front of the parameter list.现在,当您调用Dog(4, "brown")three.add(five) ,总会有一个不可见的参数,该参数将分配给参数列表前面的悬空额外参数。 It is conventionally called self , and will get the value of the object in front of the dot.它通常被称为self ,并将获取点前面的对象的值。 Thus, within the Dog's __init__ (constructor), self will be whatever the new Dog will turn out to be;因此,在 Dog 的__init__ (构造函数)中, self将成为新 Dog 将成为的任何东西; within MyInteger 's add , self will be bound to the object in the variable three .MyIntegeraddself将绑定到变量three的对象。 Thus, three.value will be the same variable outside the add , as self.value within the add .因此, three.value将外部相同的变量add ,如self.value的内add

If I say the_mangy_one = fido , I will start referring to the object known as fido with yet another name.如果我说the_mangy_one = fido ,我将开始用另一个名称来指代称为fido的对象。 From now on, fido.colour is exactly the same variable as the_mangy_one.colour .从现在开始, fido.colourthe_mangy_one.colour

So, the things inside the __init__ .所以, __init__里面的东西。 You can think of them as noting things into the Dog's birth certificate.你可以把它们看作是在狗的出生证明上记下的东西。 colour by itself is a random variable, could contain anything. colour本身是一个随机变量,可以包含任何内容。 fido.colour or self.colour is like a form field on the Dog's identity sheet; fido.colourself.colour就像是 Dog 身份证上的表单字段; and __init__ is the clerk filling it out for the first time.__init__是第一次填写它的职员。

Any clearer?有更清楚的吗?

EDIT : Expanding on the comment below:编辑:扩展下面的评论:

You mean a list of objects , don't you?你的意思是一个对象列表,不是吗?

First of all, fido is actually not an object.首先, fido实际上不是一个对象。 It is a variable, which is currently containing an object, just like when you say x = 5 , x is a variable currently containing the number five.它是一个变量,当前包含一个对象,就像你说x = 5x是一个当前包含数字 5 的变量。 If you later change your mind, you can do fido = Cat(4, "pleasing") (as long as you've created a class Cat ), and fido would from then on "contain" a cat object.如果您后来改变主意,您可以执行fido = Cat(4, "pleasing") (只要您创建了一个Cat类),从那时起fido将“包含”一个 cat 对象。 If you do fido = x , it will then contain the number five, and not an animal object at all.如果您执行fido = x ,它将包含数字 5,而根本不是动物对象。

A class by itself doesn't know its instances unless you specifically write code to keep track of them.除非您专门编写代码来跟踪它们,否则类本身并不知道其实例。 For instance:例如:

class Cat:
    census = [] #define census array

    def __init__(self, legs, colour):
        self.colour = colour
        self.legs = legs
        Cat.census.append(self)

Here, census is a class-level attribute of Cat class.这里的censusCat类的一个类级属性。

fluffy = Cat(4, "white")
spark = Cat(4, "fiery")
Cat.census
# ==> [<__main__.Cat instance at 0x108982cb0>, <__main__.Cat instance at 0x108982e18>]
# or something like that

Note that you won't get [fluffy, sparky] .请注意,您不会得到[fluffy, sparky] Those are just variable names.这些只是变量名。 If you want cats themselves to have names, you have to make a separate attribute for the name, and then override the __str__ method to return this name.如果你想让猫自己有名字,你必须为名字做一个单独的属性,然后覆盖__str__方法来返回这个名字。 This method's (ie class-bound function, just like add or __init__ ) purpose is to describe how to convert the object to a string, like when you print it out.这个方法(即类绑定函数,就像add__init__ )的目的是描述如何将对象转换为字符串,就像打印出来一样。

To contribute my 5 cents to the thorough explanation from Amadan .Amadan详尽解释贡献我的 5 美分。

Where classes are a description "of a type" in an abstract way.其中类是以抽象方式对“类型”的描述。 Objects are their realizations: the living breathing thing.对象是它们的实现:活生生的会呼吸的东西。 In the object-orientated world there are principal ideas you can almost call the essence of everything.在面向对象的世界中,有一些基本思想几乎可以称为一切事物的本质。 They are:他们是:

  1. encapsulation (won't elaborate on this)封装(这里不再详述)
  2. inheritance遗产
  3. polymorphism多态性

Objects have one, or more characteristics (= Attributes) and behaviors (= Methods).对象具有一个或多个特征(= 属性)和行为(= 方法)。 The behavior mostly depends on the characteristics.行为主要取决于特征。 Classes define what the behavior should accomplish in a general way, but as long as the class is not realized (instantiated) as an object it remains an abstract concept of a possibility.类以一般方式定义了行为应该完成什么,但只要类没有实现(实例化)为对象,它就仍然是可能性的抽象概念。 Let me illustrate with the help of "inheritance" and "polymorphism".让我借助“继承”和“多态”来说明。

    class Human:
        gender
        nationality
        favorite_drink
        core_characteristic
        favorite_beverage
        name
        age

        def love    
        def drink
        def laugh
        def do_your_special_thing                

    class Americans(Humans)
        def drink(beverage):
            if beverage != favorite_drink: print "You call that a drink?"
            else: print "Great!" 

    class French(Humans)
        def drink(beverage, cheese):
            if beverage == favourite_drink and cheese == None: print "No cheese?" 
            elif beverage != favourite_drink and cheese == None: print "Révolution!"

    class Brazilian(Humans)
        def do_your_special_thing
            win_every_football_world_cup()

    class Germans(Humans)
        def drink(beverage):
            if favorite_drink != beverage: print "I need more beer"
            else: print "Lecker!" 

    class HighSchoolStudent(Americans):
        def __init__(self, name, age):
             self.name = name
             self.age = age

jeff = HighSchoolStudent(name, age):
hans = Germans()
ronaldo = Brazilian()
amelie = French()

for friends in [jeff, hans, ronaldo]:
    friends.laugh()
    friends.drink("cola")
    friends.do_your_special_thing()

print amelie.love(jeff)
>>> True
print ronaldo.love(hans)
>>> False

Some characteristics define human beings.一些特征定义了人类。 But every nationality differs somewhat.但每个国籍都有些不同。 So "national-types" are kinda Humans with extras.所以“民族类型”是一种有额外功能的人类。 "Americans" are a type of "Humans " and inherit some abstract characteristics and behavior from the human type (base-class) : that's inheritance. “美国人”是“人类”的一种,从人类类型(基类)继承了一些抽象的特征和行为:那就是继承。 So all Humans can laugh and drink, therefore all child-classes can also!所以全人类可以笑可以喝酒,所以所有儿童班也可以! Inheritance (2).继承(2)。

But because they are all of the same kind (Type/base-class : Humans) you can exchange them sometimes: see the for-loop at the end.但是因为它们都是同一种类型(类型/基类:人类),您有时可以交换它们:请参阅最后的 for 循环。 But they will expose an individual characteristic, and thats Polymorphism (3).但是它们会暴露一个单独的特征,那就是多态性(3)。

So each human has a favorite_drink, but every nationality tend towards a special kind of drink.所以每个人都有一种最喜欢的饮料,但每个国家都倾向于一种特殊的饮料。 If you subclass a nationality from the type of Humans you can overwrite the inherited behavior as I have demonstrated above with the drink() Method.如果您从 Humans 类型子类化一个国籍,您可以覆盖继承的行为,正如我上面用drink()方法所演示的那样。 But that's still at the class-level and because of this it's still a generalization.但这仍然是在类级别,因此它仍然是一个概括。

hans = German(favorite_drink = "Cola")

instantiates the class German and I "changed" a default characteristic at the beginning.实例化 German 类,我在开始时“更改”了一个默认特性。 (But if you call hans.drink('Milk') he would still print "I need more beer" - an obvious bug ... or maybe that's what i would call a feature if i would be a Employee of a bigger Company. ;-)! (但如果你调用 hans.drink('Milk') 他仍然会打印“我需要更多啤酒”——一个明显的错误......或者如果我是一家大公司的员工,这可能就是我所说的功能。 ;-)! ) )

The characteristic of a type eg Germans (hans) are usually defined through the constructor (in python : __init__ ) at the moment of the instantiation.类型的特征,例如 Germans (hans) 通常在实例化时通过构造函数(在 python 中: __init__ )定义。 This is the point where you define a class to become an object.这是您将类定义为对象的地方。 You could say breath life into an abstract concept (class) by filling it with individual characteristics and becoming an object.你可以说,通过用个人特征填充抽象概念(类)并成为一个对象,将生命注入到抽象概念(类)中。

But because every object is an instance of a class they share all some basic characteristic-types and some behavior.但是因为每个对象都是一个类的实例,所以它们共享所有一些基本的特征类型和一些行为。 This is a major advantage of the object-orientated concept.这是面向对象概念的主要优点。

To protect the characteristics of each object you encapsulate them - means you try to couple behavior and characteristic and make it hard to manipulate it from outside the object.为了保护您封装它们的每个对象的特性 - 意味着您尝试将行为和特性结合起来,并使其难以从对象外部操纵它。 That's Encapsulation (1)这就是封装(1)

It is just to initialize the instance's variables.它只是初始化实例的变量。

Eg create a crawler instance with a specific database name (from your example above).例如,创建一个具有特定数据库名称的crawler实例(来自上面的示例)。

It seems like you need to use __init__ in Python if you want to correctly initialize mutable attributes of your instances.如果要正确初始化实例的可变属性,似乎需要在 Python 中使用__init__

See the following example:请参阅以下示例:

>>> class EvilTest(object):
...     attr = []
... 
>>> evil_test1 = EvilTest()
>>> evil_test2 = EvilTest()
>>> evil_test1.attr.append('strange')
>>> 
>>> print "This is evil:", evil_test1.attr, evil_test2.attr
This is evil: ['strange'] ['strange']
>>> 
>>> 
>>> class GoodTest(object):
...     def __init__(self):
...         self.attr = []
... 
>>> good_test1 = GoodTest()
>>> good_test2 = GoodTest()
>>> good_test1.attr.append('strange')
>>> 
>>> print "This is good:", good_test1.attr, good_test2.attr
This is good: ['strange'] []

This is quite different in Java where each attribute is automatically initialized with a new value:这在 Java 中完全不同,Java 中的每个属性都会自动用一个新值初始化:

import java.util.ArrayList;
import java.lang.String;

class SimpleTest
{
    public ArrayList<String> attr = new ArrayList<String>();
}

class Main
{
    public static void main(String [] args)
    {
        SimpleTest t1 = new SimpleTest();
        SimpleTest t2 = new SimpleTest();

        t1.attr.add("strange");

        System.out.println(t1.attr + " " + t2.attr);
    }
}

produces an output we intuitively expect:产生我们直观期望的输出:

[strange] []

But if you declare attr as static , it will act like Python:但是,如果您将attr声明为static ,它将像 Python 一样运行:

[strange] [strange]

Following with your car example: when you get a car, you just don't get a random car, I mean, you choose the color, the brand, number of seats, etc. And some stuff is also "initialize" without you choosing for it, like number of wheels or registration number.以你的汽车为例:当你得到一辆车时,你不会得到一辆随机的汽车,我的意思是,你选择颜色、品牌、座位数量等。有些东西也是“初始化”而无需你选择对于它,例如车轮数量或注册号。

class Car:
    def __init__(self, color, brand, number_of_seats):
        self.color = color
        self.brand = brand
        self.number_of_seats = number_of_seats
        self.number_of_wheels = 4
        self.registration_number = GenerateRegistrationNumber()

So, in the __init__ method you defining the attributes of the instance you're creating.因此,在__init__方法中,您定义正在创建的实例的属性。 So, if we want a blue Renault car, for 2 people, we would initialize or instance of Car like:所以,如果我们想要一辆蓝色的雷诺汽车,对于 2 个人,我们会初始化Car实例,如:

my_car = Car('blue', 'Renault', 2)

This way, we are creating an instance of the Car class.这样,我们就创建了一个Car类的实例。 The __init__ is the one that is handling our specific attributes (like color or brand ) and its generating the other attributes, like registration_number . __init__是处理我们的特定属性(如colorbrand )并生成其他属性(如registration_number )的那个。

Classes are objects with attributes (state, characteristic) and methods (functions, capacities) that are specific for that object (like the white color and fly powers, respectively, for a duck).类是具有特定于该对象的属性(状态、特征)和方法(功能、能力)的对象(分别像鸭子的白色和飞行能力)。

When you create an instance of a class, you can give it some initial personality (state or character like the name and the color of her dress for a newborn).创建类的实例时,可以为其赋予一些初始个性(状态或角色,例如新生儿的姓名和衣服颜色)。 You do this with __init__ .你用__init__做到这一点。

Basically __init__ sets the instance characteristics automatically when you call instance = MyClass(some_individual_traits) .基本上__init__在您调用instance = MyClass(some_individual_traits)时自动设置实例特征。

The __init__ function is setting up all the member variables in the class. __init__函数正在设置类中的所有成员变量。 So once your bicluster is created you can access the member and get a value back:因此,一旦您的 bicluster 被创建,您就可以访问该成员并获得一个值:

mycluster = bicluster(...actual values go here...)
mycluster.left # returns the value passed in as 'left'

Check out the Python Docs for some info.查看Python 文档以获取一些信息。 You'll want to pick up an book on OO concepts to continue learning.您需要拿起一本关于 OO 概念的书来继续学习。

class Dog(object):

    # Class Object Attribute
    species = 'mammal'

    def __init__(self,breed,name):
        self.breed = breed
        self.name = name

In above example we use species as a global since it will be always same(Kind of constant you can say).在上面的例子中,我们使用物种作为全局,因为它总是相同的(你可以说是一种常数)。 when you call __init__ method then all the variable inside __init__ will be initiated(eg:breed,name).当你调用__init__方法,然后里面的所有变量__init__将启动(例如:品种,名称)。

class Dog(object):
    a = '12'

    def __init__(self,breed,name,a):
        self.breed = breed
        self.name = name
        self.a= a

if you print the above example by calling below like this如果您通过如下调用来打印上面的示例

Dog.a
12

Dog('Lab','Sam','10')
Dog.a
10

That means it will be only initialized during object creation.这意味着它只会在对象创建期间初始化。 so anything which you want to declare as constant make it as global and anything which changes use __init__所以任何你想声明为常量的东西都让它成为全局的,任何改变的东西都使用__init__

__init__() can: __init__()可以:

  • initialize the instance of class.初始化 class 的实例。
  • be called many time.多次被调用。
  • only return None .只返回None

For example, Person class has __init__() as shown below:例如, Person class 有__init__()如下所示:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        print('"__init__()" is called.')

Now, we create and initialize the instance of Person class as shown below:现在,我们创建并初始化Person class 的实例,如下所示:

       # Here
obj = Person("John", 27)

Then, __init__() is called as shown below:然后,调用__init__()如下所示:

"__init__()" is called.

Next, we check if name and age are initialized as shown below:接下来,我们检查nameage是否已初始化,如下所示:

obj = Person("John", 27)
print(obj.name) # Here
print(obj.age) # Here

Then, name and age are initialized as shown below:然后, nameage被初始化,如下所示:

"__init__()" is called.
John # Here
27   # Here

And, __init__() can be called many times as shown below:并且, __init__()可以被多次调用,如下所示:

obj = Person("John", 27)
print(obj.__init__("Tom", "18")) # Here
print(obj.name)
print(obj.age)

Then, __init__() is called and the instance of Person class is reinitialized and None is returned from __init__() as shown below:然后,调用__init__()并重新初始化Person class 的实例,并从__init__()返回None ,如下所示:

"__init__()" is called.
"__init__()" is called.
None
Tom
18

Lastly, if __init__() doesn't return None and we call __init__() as shown below:最后,如果__init__()不返回None并且我们调用__init__()如下所示:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        print('"__init__()" is called.')
        return "Hello" # Here

obj = Person("John", 27) # Here

The error below occurs:出现以下错误:

TypeError: init () should return None, not 'str' TypeError: init () 应该返回 None,而不是 'str'

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

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