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

Los*_*oul 113 python class

我无法理解类的初始化.

它们有什么意义,我们如何知道它们包含哪些内容?在类中编写需要不同类型的思考而不是创建函数(我想我可以创建函数,然后将它们包装在一个类中,这样我就可以重用它们.这会起作用吗?)

这是一个例子:

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()
Run Code Online (Sandbox Code Playgroud)

或者另一个代码示例:

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
Run Code Online (Sandbox Code Playgroud)

__init__在尝试阅读其他人的代码时遇到了很多类,但我不了解创建它们的逻辑.

Ama*_*dan 273

通过你所写的内容,你错过了一个重要的理解:一个类和一个对象之间的区别.__init__不初始化类,它初始化类或对象的实例.每只狗都有颜色,但狗作为一个类没有.每只狗有四个或更少的脚,但是这类狗没有.该类是对象的概念.当你看到Fido和Spot时,你会发现它们的相似之处,它们的犬友.那是班级.

当你说

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

fido = Dog(4, "brown")
spot = Dog(3, "mostly yellow")
Run Code Online (Sandbox Code Playgroud)

你说,Fido是一条有4条腿的棕色狗,而Spot有点像跛脚,大多是黄色的.该__init__函数称为构造函数或初始化函数,并在创建类的新实例时自动调用.在该函数中,新创建的对象被分配给参数self.符号self.legslegs变量中对象的一个属性self.属性类似于变量,但它们描述了对象的状态,或对象可用的特定动作(函数).

但是,请注意,您没有colour为狗狗本身设置- 这是一个抽象的概念.有些属性在类上有意义.例如,population_size就是这样 - 计算Fido是没有意义的,因为Fido总是一个.数狗是有道理的.让我们说世界上有2亿只狗.这是Dog类的财产.Fido与2亿号无关,Spot也没有.它被称为"类属性",而不是"实例属性" colourlegs更高.

现在,对于更少的犬和更多的编程相关的东西.正如我在下面写的那样,添加东西的课程是不明智的 - 它是什么类?Python中的类由不同数据的集合组成,其行为类似.一类狗由Fido和Spot以及199999999998其他与它们相似的动物组成,它们都在路灯柱上撒尿.添加东西的课程包含哪些内容?根据他们固有的数据,他们有什么不同?他们分享了什么行动?

然而,数字......那些是更有趣的主题.说,整数.它们中有很多,比狗多得多.我知道Python已经有了整数,但让我们玩笨并再次"实现"它们(通过欺骗和使用Python的整数).

所以,整数是一个类.他们有一些数据(值)和一些行为("把我加到另一个数字").让我们来看看:

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
Run Code Online (Sandbox Code Playgroud)

这有点脆弱(我们假设other将是一个MyInteger),但我们现在将忽略.在实际代码中,我们不会; 我们测试它以确保,甚至可能强制它("你不是一个整数?通过golly,你有10纳秒成为一个!9 ... 8 ....")

我们甚至可以定义分数.分数也知道如何添加自己.

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)
Run Code Online (Sandbox Code Playgroud)

有更多的分数而不是整数(不是真的,但计算机不知道这一点).我们来两个:

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

你实际上并没有在这里宣布任何事情.属性就像一种新的变量.正常变量只有一个值.让我们说你写colour = "grey".你不能有一个名为另一个变量colour"fuchsia"-并非在代码相同的地方.

数组在一定程度上解决了这个问题.如果你说colour = ["grey", "fuchsia"],你已经将两种颜色堆叠到变量中,但你可以通过它们的位置来区分它们(在这种情况下为0或1).

属性是绑定到对象的变量.与数组一样,我们可以在不同的狗上有很多colour变量.所以,是一个变量,但是另一个变量.第一个绑定到变量中的对象; 第二,.现在,当你打电话时,或者,总会有一个不可见的参数,它将被分配给参数列表前面的悬空额外参数.它通常被称为,并将获得点前面的对象的值.因此,在Dog的(构造函数)中,将是新的狗将会成为什么; 在's内,将绑定到变量中的对象.因此,将外部的相同的变量,作为内.fido.colourspot.colourfidospotDog(4, "brown")three.add(five)self__init__selfMyIntegeraddselfthreethree.valueaddself.valueadd

如果我说the_mangy_one = fido,我将开始引用fido另一个名称的对象.从现在开始,fido.colour变量与变量完全相同the_mangy_one.colour.

所以,里面的东西__init__.你可以把它们想象成狗的出生证明.colour它本身就是一个随机变量,可以包含任何东西.fido.colour或者self.colour就像狗的身份证上的表格字段; 并且__init__是第一次填写它的职员.

有更清楚的吗?

编辑:扩展以下评论:

你的意思是一个对象列表,不是吗?

首先,fido实际上不是一个对象.它是一个变量,当前包含一个对象,就像你说的那样x = 5,x是一个当前包含数字5的变量.如果你以后改变主意,你可以做fido = Cat(4, "pleasing")(只要你创建了一个类Cat),fido然后从那时起"包含"一个猫对象.如果这样做fido = x,它将包含数字5,而不是动物对象.

除非您专门编写代码来跟踪它们,否则类本身不知道它的实例.例如:

class Cat:
    census = [] #define census array

    def __init__(self, legs, colour):
        self.colour = colour
        self.legs = legs
        Cat.census.append(self)
Run Code Online (Sandbox Code Playgroud)

这里census是类的类级属性Cat.

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

请注意,你不会得到[fluffy, sparky].这些只是变量名.如果您希望猫自己拥有名称,则必须为名称创建单独的属性,然后重写该__str__方法以返回此名称.此方法(即类绑定函数,就像add__init__)目的是描述如何将对象转换为字符串,就像打印出来一样.

  • 哇谢谢......这实际上给我带来了很多意义,所以任何使它成为现实的东西,我需要在init函数中预先声明.在这种情况下,狗,有腿和颜色.例如,如果我创建了一个添加两个数字的类,我会声明self.firstnumber和self.secondnumber然后在类中稍后执行firstnumber + secondnumber以获得答案? (7认同)
  • 谢谢你的惊人答案.我现在看到并理解课程的力量.对不起,如果听起来很蠢.你只是我意识到我可以对数据进行排序并同时维护许多不同事物的状态(而我只能跟踪我可以创建的变量或通过循环更多的变量).那么说,我需要弄清楚每只狗的平均腿数?有没有办法检索我用类创建的所有对象的列表,这样我可以开始这样的calucation?或者我是否应该维护我创建的类列表(即[fido,spot]) (3认同)
  • 有点儿。你可以这么做。但仅仅为了添加东西而创建一个类几乎没有意义。类通常用行为来实现数据——纯粹的行为只是函数。我将用相关的内容来扩展答案;稍等一会。 (2认同)

Don*_*ion 23

Amadan详细解释贡献我5美分.

其中类是抽象方式的"类型"描述.物体是他们的实现:活生生的呼吸物.在面向对象的世界中,有一些主要的想法,你几乎可以称之为一切的本质.他们是:

  1. 封装(不会详细说明)
  2. 遗产
  3. 多态性

对象具有一个或多个特征(=属性)和行为(=方法).行为主要取决于特征.类定义了行为应该以一般方式完成的内容,但只要该类未被实现(实例化)为对象,它仍然是一种可能性的抽象概念.让我借助"继承"和"多态"来说明.

    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
Run Code Online (Sandbox Code Playgroud)

一些特征定义了人类.但每个国籍都有所不同.因此,"国家类型"是有点额外的人类."美国人"是一种"人类",并从人类(基类)继承了一些抽象的特征和行为:这是继承.因此,所有人类都可以笑喝,因此所有的儿童班也可以!继承(2).

但是因为它们都是同一种类型(类型/基类:人类),你有时可以交换它们:最后看到for循环.但是它们会暴露出个体特征,那就是多态性(3).

所以每个人都有一个最喜欢的饮料,但每个国家都倾向于一种特殊的饮料.如果你从人类的类型中继承国籍,你可以覆盖继承的行为,正如我在上面用drink()方法论证的那样.但这仍然处于阶级水平,因此它仍然是一个概括.

hans = German(favorite_drink = "Cola")
Run Code Online (Sandbox Code Playgroud)

实例化德语类,我在开头"改变"了默认特征.(但如果你打电话给hans.drink('牛奶'),他仍会打印"我需要更多啤酒" - 这是一个明显的错误......或者如果我是一家大公司的员工,这就是我所说的功能. ;-)!)

例如德国人(hans)类型的特征通常__init__在实例化时通过构造函数(在python :)中定义.这是您将类定义为对象的点.你可以通过填充个人特征并成为一个对象,将呼吸生命描述为抽象概念(类).

但是因为每个对象都是一个类的实例,所以它们共享所有一些基本的特征类型和一些行为.这是面向对象概念的主要优点.

为了保护你封装它们的每个对象的特性 - 意味着你试图将行为和特征结合起来,并且难以从对象外部操纵它.这是封装(1)


jld*_*ont 5

它只是初始化实例的变量.

例如,创建一个crawler具有特定数据库名称的实例(来自上面的示例).


jul*_*ria 5

以你的汽车为例:当你得到一辆车时,你不会得到一辆随机的汽车,我的意思是,你选择颜色、品牌、座位数等。有些东西也是“初始化”的,无需你选择例如车轮数量或注册号。

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()
Run Code Online (Sandbox Code Playgroud)

因此,在__init__您定义正在创建的实例的属性的方法中。因此,如果我们想要一辆蓝色雷诺汽车,供 2 人使用,我们将初始化或Car类似的实例:

my_car = Car('blue', 'Renault', 2)
Run Code Online (Sandbox Code Playgroud)

这样,我们就创建了该类的一个实例Car。是__init__处理我们的特定属性(例如colorbrand)并生成其他属性(例如 )的属性registration_number


ale*_*ine 5

__init__如果你想正确初始化实例的可变属性,你似乎需要在Python中使用。

请参见以下示例:

>>> 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'] []
Run Code Online (Sandbox Code Playgroud)

这在 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

产生我们直观期望的输出:

[strange] []
Run Code Online (Sandbox Code Playgroud)

但如果你声明attrstatic,它将像 Python 一样运行:

[strange] [strange]
Run Code Online (Sandbox Code Playgroud)