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

时间:2011-12-22 20:06:39

标签: 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()

或其他代码示例:

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

在尝试阅读其他人的代码时遇到__init__这么多的课程,但我不明白创建它们的逻辑。

8 个答案:

答案 0 :(得分:266)

通过你所写的内容,你错过了一个重要的理解:一个类和一个对象之间的区别。 __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")

你说,Fido是一只有四条腿的棕色狗,而Spot有点像跛脚,大多是黄色的。 __init__函数称为构造函数或初始化函数,并在创建类的新实例时自动调用。在该函数中,新创建的对象被分配给参数self。符号self.legs是变量legs中对象的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

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

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

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)

除了整数之外还有更多的分数(不是真的,但计算机不知道这一点)。让我们两个:

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

你实际上并没有在这里宣布任何事情。属性就像一种新的变量。正常变量只有一个值。我们假设你写colour = "grey"。您不能拥有另一个名为colour的{​​{1}}变量 - 不在代码中的相同位置。

阵列在一定程度上解决了这个问题。如果您说"fuchsia",您已将两种颜色堆叠到变量中,但您可以通过它们的位置(在这种情况下为0或1)来区分它们。

属性是绑定到对象的变量。与数组一样,我们可以在不同的狗上有很多colour = ["grey", "fuchsia"]个变量,。因此,colour是一个变量,但fido.colour是另一个变量。第一个绑定到变量spot.colour中的对象;第二个,fido。现在,当您致电spotDog(4, "brown")时,将始终存在一个不可见的参数,该参数将分配给参数列表前面的悬空额外参数。它通常被称为three.add(five),它将获得点前面的对象的值。因此,在狗的self(构造函数)中,__init__将成为新狗将会成为的任何东西;在self MyInteger范围内,add将绑定到变量self中的对象。因此,three将在three.value之外的同一变量,add内的self.value

如果我说add,我将开始使用另一个名称来引用称为the_mangy_one = fido的对象。从现在开始,fidofido.colour完全相同。

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

更清楚吗?

编辑:扩展以下评论:

您的意思是对象列表,不是吗?

首先,__init__实际上不是一个对象。它是一个变量,当前包含一个对象,就像你说fido时一样,x = 5是一个当前包含数字5的变量。如果您以后改变主意,可以x(只要您已经创建了一个类fido = Cat(4, "pleasing")),然后Cat将来自"包含&# 34;猫对象。如果你做fido,那么它将包含数字5,而不是动物对象。

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

fido = x

此处,class Cat: census = [] #define census array def __init__(self, legs, colour): self.colour = colour self.legs = legs Cat.census.append(self) 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 。这些只是变量名。如果您希望猫自己拥有名称,则必须为名称创建单独的属性,然后覆盖[fluffy, sparky]方法以返回此名称。这个方法(即类绑定函数,就像__str__add)目的是描述如何将对象转换为字符串,就像打印出来一样。

答案 1 :(得分:23)

thorough explanation from Amadan贡献我的5美分。

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

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

        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
    

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

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

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

    hans = German(favorite_drink = "Cola")
    

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

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

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

    为了保护封装它们的每个对象的特性 - 意味着您尝试将行为和特征耦合在一起,并且难以从对象外部操纵它。这是封装(1)

答案 2 :(得分:5)

只是初始化实例的变量。

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

答案 3 :(得分:3)

关注您的汽车示例:当您开车时,您只是没有随机购买汽车,我的意思是,您选择的颜色,品牌,座位数等等。有些东西也是“初始化”而你没有选择它,比如轮数或注册号。

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()

因此,在__init__方法中,您可以定义要创建的实例的属性。所以,如果我们想要一辆蓝色雷诺汽车,对于2个人,我们会初始化或Car的实例,如:

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

这样,我们正在创建Car类的实例。 __init__是处理我们的特定属性(例如colorbrand)并生成其他属性的registration_number,如{{1}}。

答案 4 :(得分:3)

类是具有属性(状态,特征)和特定于该对象的方法(函数,容量)的对象(如鸭子的白色和苍蝇力)。

当你创建一个类的实例时,你可以给它一些初始的个性(状态或字符,如新生儿的名字和颜色)。您可以使用__init__执行此操作。

基本上__init__会在您调用instance = MyClass(some_individual_traits)时自动设置实例特征。

答案 5 :(得分:3)

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

请参阅以下示例:

>>> 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'] []

这在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);
    }
}

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

[strange] []

但是如果你将attr声明为static,它将像Python一样:

[strange] [strange]

答案 6 :(得分:2)

__init__函数正在设置类中的所有成员变量。因此,一旦创建了bicluster,您就可以访问该成员并获得一个值:

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

查看Python Docs以获取一些信息。你想拿起一本关于OO概念的书来继续学习。

答案 7 :(得分:1)

class Dog(object):

    # Class Object Attribute
    species = 'mammal'

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

在上面的例子中,我们使用物种作为全局物种,因为它总是相同的(你可以说是一种常数)。当你调用__init__方法时,__init__内的所有变量都将被启动(例如:品种,名称)。

class Dog(object):
    a = '12'

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

如果您通过以下方式调用上面的示例打印上面的

Dog.a
12

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

这意味着它只会在对象创建期间初始化。所以你想要声明为常量的东西使它成为全局的,任何改变的东西都使用__init__