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

Why do we use __init__ in Python classes?

我无法理解类的初始化。

它们的意义是什么?我们如何知道在它们中包含什么?在类中写作是否需要一种不同类型的思考,而不是创建函数(我认为我可以只创建函数,然后将它们包装在类中,这样我就可以重用它们了)。这样行吗?)

下面是一个例子:

1
2
3
4
5
6
7
8
9
10
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()

或其他代码示例:

1
2
3
4
5
6
7
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__的类,但我不理解创建它们的逻辑。


根据你所写的,你缺少了一个关键的理解:类和对象之间的区别。__init__不初始化类,它初始化类或对象的实例。每只狗都有颜色,但作为一个阶级的狗没有。每只狗有四只或更少的脚,但狗的阶级没有。阶级是一个物体的概念。当你看到菲多和斯波特时,你会发现他们的相似之处,他们的教条。这就是课堂。好的。

当你说好的。

1
2
3
4
5
6
7
class Dog:
    def __init__(self, legs, colour):
        self.legs = legs
        self.colour = colour

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

你是说,菲多是一只棕色的四条腿的狗,而斑点是一个有点残废,大多是黄色的。__init__函数被称为构造函数或初始值设定项,并在创建类的新实例时自动调用。在该函数中,新创建的对象被分配给参数self。符号self.legs是变量self中对象的一个名为legs的属性。属性有点像变量,但它们描述对象的状态,或对象可用的特定操作(函数)。好的。

但是,请注意,您没有为教条本身设置colour–这是一个抽象的概念。在类上有一些有意义的属性。例如,population_size就是这样的一种——把fido数出来是没有意义的,因为fido总是一个。数狗是有意义的。假设世界上有2亿只狗。它是狗类的财产。菲多和2亿没有任何关系,斯波特也没有。它被称为"类属性",与上面的colourlegs的"实例属性"不同。好的。

现在,对于一些与狗和编程相关的东西来说。正如我在下面写的,类添加东西是不明智的-它是什么类?Python中的类由不同的数据集合组成,它们的行为类似。狗类包括fido和spot以及19999999998其他类似的动物,它们都在灯柱上撒尿。添加内容的类由什么组成?它们固有的数据有什么不同?他们共同采取了哪些行动?好的。

但是,数字…这些是比较有趣的科目。比如说整数。他们有很多,比狗还多。我知道python已经有了整数,但是让我们装傻,再次"实现"它们(通过欺骗和使用python的整数)。好的。

所以,整数是一个类。他们有一些数据(价值)和一些行为("把我加到另一个数字上")。让我们展示一下:好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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),但现在我们将忽略它。在真正的代码中,我们不会;我们会测试它以确保,甚至可能强制它("您不是整数?"天哪,你有10纳秒的时间变成一个!9…8……")好的。

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

1
2
3
4
5
6
7
8
9
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)

分数甚至比整数还要多(不是真的,但计算机不知道)。让我们做两个:好的。

1
2
3
4
5
6
7
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的变量,即"fuchsia"—不在同一位置。好的。

数组在一定程度上解决了这个问题。如果你说colour = ["grey","fuchsia"],你已经在变量中叠加了两种颜色,但是你根据它们的位置(在本例中是0或1)来区分它们。好的。

属性是绑定到对象的变量。像数组一样,我们可以在不同的狗上有大量的colour变量。因此,fido.colour是一个变量,而spot.colour是另一个变量。第一个绑定到变量fido中的对象;第二个绑定到变量spot中的对象。现在,当您调用Dog(4,"brown")three.add(five)时,总是会有一个不可见的参数,它将被分配给参数列表前面悬空的额外参数。它通常被称为self,将得到点前面的对象的值。因此,在狗的__init__(构造器)中,self将是新狗将变成的任何东西;在MyIntegeradd中,self将绑定到变量three中的对象。因此,three.valueadd之外与self.valueadd内是相同的变量。好的。

如果我说the_mangy_one = fido,我会用另一个名字来指这个叫fido的物体。从现在起,fido.colourthe_mangy_one.colour的变量完全相同。好的。

所以,在__init__里的东西。你可以把它们看作是在狗的出生证明上做了记号。colour本身就是一个随机变量,可以包含任何东西。fido.colourself.colour就像狗身份证上的一个表单字段;__init__是第一次填写表单的职员。好的。

更清楚些吗?好的。

编辑:展开以下评论:好的。

你是说一张物品清单,不是吗?好的。

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

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

1
2
3
4
5
6
7
class Cat:
    census = [] #define census array

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

这里,censusCat类的一个类级属性。好的。

1
2
3
4
5
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]。这些只是变量名。如果希望cats本身具有名称,则必须为名称创建一个单独的属性,然后重写__str__方法以返回此名称。此方法(即类绑定函数,就像add__init__一样)的目的是描述如何将对象转换为字符串,就像打印出来时一样。好的。好啊。


为阿玛丹的彻底解释贡献我的5美分。

其中类是抽象的"类型"描述。物体是它们的意识:活生生的呼吸物。在面向对象的世界里,你几乎可以称之为一切事物的本质。他们是:

  • 封装(不详细说明)
  • 遗传
  • 多态性
  • 对象具有一个或多个特征(=属性)和行为(=方法)。行为主要取决于特征。类以一般的方式定义行为应该完成什么,但是只要类没有作为对象实现(实例化),它仍然是一个抽象的可能性概念。让我用"继承"和"多态性"来说明。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
        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()方法所演示的那样。但这仍然是在课堂上,因此它仍然是一个泛化。

    1
    hans = German(favorite_drink ="Cola")

    实例化类German,并在开始时"更改"默认特性。(但如果你叫汉斯喝牛奶),他还是会印上"我需要更多的啤酒"-一个明显的错误…或者,如果我是一家大公司的员工,这可能就是我所说的功能。!)

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

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

    为了保护每个对象的特性,您封装了它们——意味着您试图将行为和特性结合起来,并使从对象外部对其进行操作变得困难。这就是封装(1)


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

    例如,使用特定的数据库名称(从上面的示例中)创建一个crawler实例。


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

    请参见以下示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    >>> 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中是完全不同的,其中每个属性都用一个新的值自动初始化:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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);
        }
    }

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

    1
    [strange] []

    但是,如果您声明attrstatic,它将像python:

    1
    [strange] [strange]

    类是具有特定于该对象的属性(状态、特征)和方法(函数、容量)的对象(分别为duck的white color和fly powers)。

    当你创建一个类的实例时,你可以给它一些初始的个性(状态或角色,比如新生儿的名字和衣服的颜色)。你用__init__做这个。

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


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

    1
    2
    3
    4
    5
    6
    7
    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__方法中,您定义要创建的实例的属性。因此,如果我们想要一辆蓝色的雷诺汽车,对于两个人来说,我们将初始化或实例Car,比如:

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

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

    • 关于python中类的更多信息
    • 关于__init__方法的更多信息


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

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

    查看python文档了解一些信息。你会想拿起一本关于OO概念的书继续学习。


    1
    2
    3
    4
    5
    6
    7
    8
    class Dog(object):

        # Class Object Attribute
        species = 'mammal'

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

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

    1
    2
    3
    4
    5
    6
    7
    class Dog(object):
        a = '12'

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

    如果您像这样调用下面的代码来打印上面的示例

    1
    2
    3
    4
    5
    6
    Dog.a
    12

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

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