为什么在Python类中使用“__init__”?

xhv8bpkk  于 2022-11-19  发布在  Python
关注(0)|答案(9)|浏览(196)

我在理解类的初始化时遇到了困难。
它们有什么意义?我们如何知道它们要包含什么?在类中编写代码与创建函数需要不同类型的思维吗?(我想我可以只创建函数,然后将它们 Package 在类中,这样我就可以重用它们了。这样行吗?)
下面是一个例子:

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

zaq34kh6

zaq34kh61#

通过你所写的,你错过了一个关键的理解片段:类和对象之间的区别。__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是变量self中对象的名为legs的属性。属性有点像变量,但它们描述对象的状态,或对象可用的特定操作(功能)。
但是,注意不要为doghood本身设置colour--这是一个抽象的概念。population_size就是其中之一--计算Fido没有意义,因为Fido总是1。计算狗确实有意义。假设世界上有2亿只狗。这是Dog类的属性。Fido和2亿这个数字无关,Spot也一样,它被称为“类属性”,而不是colourlegs以上的“示例属性”。
现在,我们来谈谈与犬类关系不大、与编程关系更密切的东西。正如我在下面所写的,添加东西的class是不明智的--它是什么类?Python中的类由行为相似的不同数据的集合组成。Class of dogs由Fido和Spot以及199999999998其他与它们相似的动物组成。他们都在路灯柱上撒尿。2添加东西的类是由什么组成的?3他们有什么固有的数据不同?4他们有什么共同的动作?
然而,数字......这些是更有趣的主题。比如说,整数。它们有很多,比狗多得多。我知道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),但是我们现在忽略它。我们将测试它以确保它是正确的,甚至可能强制它(“你不是整数?天哪,你有10纳秒的时间变成1!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)

分数的数量甚至比整数的数量还要多(实际上并不是这样,但计算机并不知道这一点)。让我们假设两个:

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,并将获取点前面的对象的值。因此,在Dog的__init__中(构造函数),self将是新的Dog将变成的任何形式;在MyIntegeradd中,self将绑定到变量three中的对象。因此,three.valueadd之外将是与add中的self.value相同的变量。
如果我说the_mangy_one = fido,我将开始用另一个名称引用名为fido的对象,从现在开始,fido.colourthe_mangy_one.colour是完全相同的变量。
__init__里面的东西,你可以把它们想象成狗的出生证明上的注解,colour本身是一个随机变量,可以包含任何内容,fido.colourself.colour就像是狗的身份表上的一个表单字段;而__init__是职员第一次填写它。
清楚了吗?

EDIT:扩展以下注解:

你是说一张物品清单吧

首先,fido实际上不是一个对象,它是一个变量,当前包含一个对象,就像你说x = 5时,x是一个当前包含数字5的变量,如果你后来改变了主意,你可以做fido = Cat(4, "pleasing")(只要你已经创建了一个类Cat),并且fido从那时起就“包含”了一个cat对象,如果你创建了fido = x,它将包含数字5,而不是一个animal对象。
一个类本身并不知道它的示例,除非你专门编写代码来跟踪它们。例如:

class Cat:
    census = [] #define census array

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

这里,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

注意,你不会得到[fluffy, sparky]。那些只是变量名。如果你想让猫自己有名字,你必须为名字创建一个单独的属性,然后重写__str__方法来返回这个名字。这个方法(也就是类绑定函数,就像add__init__一样)目的是描述如何将对象转换为字符串,就像打印出来时一样。

dxpyg8gm

dxpyg8gm2#

thorough explanation from Amadan贡献我的5美分。
其中类是以抽象方式对“类型”的描述。对象是它们的实现:在面向对象的世界里,有一些基本的概念,你几乎可以称之为一切事物的本质。它们是:
1.封装(不详细说明)
1.继承性
1.多态现象
对象有一个或多个特性(=属性)和行为(=方法)。行为主要取决于特性。类定义了行为应该完成的一般方式,但只要类没有实现(示例化)为对象,它仍然是一个抽象的概念的可能性。让我来说明“继承”和“多态性”的帮助。

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)。
但由于它们都是同一类(Type/基类:人类)你有时可以交换它们:我看到了最后的for循环,但是它们会暴露出一个个体特征,那就是多态性。
所以每个人都有一个最喜欢的韩非,但是每个民族都倾向于一种特殊的韩非。如果你从人类的类型中子类化一个民族,你可以覆盖继承的行为,就像我在上面用drink()方法演示的那样。但是这仍然是在类级别上,因此它仍然是一个泛化。

hans = German(favorite_drink = "Cola")

示例化类German,并且我在开始时“改变”了一个默认特性。(但是如果你调用hans.drink('Milk'),他仍然会输出“I need more beer”--一个明显的bug......或者如果我是一个更大公司的雇员,这可能就是我所称的特性。- -)!)
一个类型的特征,例如German(hans)通常通过构造函数定义(在python中:__init__)。这是你定义一个类成为一个对象的地方。你可以说通过用个体特征填充一个抽象概念(类)并使其成为一个对象来赋予它生命。
但是因为每个对象都是类的一个示例,它们共享所有的一些基本特征类型和一些行为,这是面向对象概念的一个主要优点。
为了保护每个对象的特性,你要封装它们--这意味着你要尝试将行为和特性结合起来,使之难以从对象外部进行操作。

zaq34kh6

zaq34kh63#

它只是初始化示例的变量。
例如,创建一个具有特定数据库名称的crawler示例(来自上面的示例)。

kadbb459

kadbb4594#

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

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的或示例,如下所示:

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

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

qojgxg4l

qojgxg4l5#

如果你想正确地初始化示例的可变属性,似乎需要在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中是完全不同的,在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]
mlnl4t2r

mlnl4t2r6#

__init__函数设置了类中的所有成员变量,因此一旦创建了双集群,就可以访问成员并返回值:

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

查看Python文档以获得一些信息。您可能会想要拿起一本关于OO概念的书来继续学习。

50pmv0ei

50pmv0ei7#

类是具有特定于该对象的属性(状态、特征)和方法(函数、能力)的对象(就像鸭子的白色和飞翔能力)。
当你创建一个类的示例时,你可以给予它一些初始的个性(状态或角色,比如新生儿的名字和衣服的颜色),你可以用__init__来完成。
基本上,当您调用instance = MyClass(some_individual_traits)时,__init__会自动设置示例特性。

m2xkgtsf

m2xkgtsf8#

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__

ubof19bj

ubof19bj9#

**__init__()**可以:

  • 初始化类别的执行严修。
  • 被多次调用。
  • 只返回None

例如,Person类具有__init__(),如下所示:

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

现在,我们创建并初始化Person类的示例,如下所示:

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

然后调用__init__(),如下所示:

"__init__()" is called.

接下来,我们检查nameage是否已初始化,如下所示:

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

然后,nameage被初始化,如下所示:

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

并且,__init__()可以被多次调用,如下所示:

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

然后,调用__init__(),重新初始化Person类的示例,并从__init__()返回None,如下所示:

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

最后,如果__init__()没有返回None,我们调用__init__(),如下所示:

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

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

出现以下错误:
类型错误:init()应该传回None,而不是'str'

相关问题