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

我无法理解类的初始化。

他们有什么重点,我们怎么知道要包含什么? 在类中写作需要不同types的思维与创build函数(我想我可以创build函数,然后把它们包装在一个类中,这样我就可以重新使用它们了,这是否行得通?

这是一个例子:

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__这么多的类,但我不明白创build它们的逻辑。

通过你所写的,你错过了一个关键的理解:一个类和一个对象之间的差异。 __init__不初始化一个类,它初始化一个类或一个对象的实例。 每只狗都有颜色,但是作为一个class级的狗不会。 每只狗有四只或更less的脚,但是这类狗不会。 这个类是一个对象的概念。 当你看到Fido和Spot时,你会认识到他们的相似性,他们的小狗。 这是class级。

当你说

 class Dog: def __init__(self, legs, colour): self.legs = legs self.colour = colour fido = Dog(4, "brown") spot = Dog(3, "mostly yellow") 

你说的是,Fido是一条4条腿的棕色狗,Spot是一个跛子,大部分是黄色的。 __init__函数被称为构造函数或初始化函数,并在创build类的新实例时自动调用。 在该函数中,新创build的对象被分配给参数self 。 记号self.legs是在variablesself称为对象的legs的属性。 属性有点像variables,但它们描述对象的状态,或对象可用的特定动作(函数)。

但是,请注意,您不为自己的狗狗设置colour – 这是一个抽象的概念。 有些属性在类中是有意义的。 例如, population_size就是这样一个数字,因为Fido总是一个,所以数出Fido是没有意义的。 数狗是有道理的。 让我们说,世界上有两亿只狗。 这是Dog类的财产。 菲多与2亿人没有关系,现货也没有。 它被称为“类属性”,而不是上面的colour或者legs “实例属性”。

现在,更less的犬和更多的编程有关。 正如我在下面写的,增加事物类是不明智的 – 这是一类什么? Python中的类组成不同数据的集合,其行为相似。 狗类包括Fido和Spot以及其他与它们相似的动物,所有动物都在灯柱上撒尿。 添加东西的类是由什么组成的? 他们固有的数据有什么不同? 他们分享什么行动?

但是,数字…这些是更有趣的主题。 说,整数。 有很多,比狗多得多。 我知道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),但我们现在将忽略。 在真实的代码中,我们不会; 我们会testing它来确定,甚至可能会强制它(“你不是一个整数?由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) 

还有更多的分数比整数(不是真的,但电脑不知道)。 我们来做两个:

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

你实际上并没有在这里宣布任何东西。 属性就像一种新的variables。 正态variables只有一个值。 让我们说你写colour = "grey" 。 你不能有另一个名为colourvariables是"fuchsia" – 不在代码中的相同位置。

arrays在一定程度上解决了这个问题。 如果你说colour = ["grey", "fuchsia"] ,你已经在variables中叠加了两种颜色,但是通过它们的位置(在这种情况下为0或者1)来区分它们。

属性是绑定到对象的variables。 和数组一样,我们可以在不同的狗上有很多colourvariables。 所以, fido.colour是一个variables,但spot.colour是另一个。 第一个绑定在variablesfido内的对象; 第二, spot 。 现在,当你调用Dog(4, "brown")three.add(five) ,总会有一个不可见的参数,它将被分配给参数列表前面的悬挂的额外的参数。 它通常被称为self ,并将得到点前面的对象的值。 因此,在狗的__init__ (构造函数)中,无论新狗会变成什么样, 在MyIntegeraddself将被绑定到variablesthree的对象。 因此, three.value将会是add之外的相同variables,如self.value中的self.value

如果我说the_mangy_one = fido ,我会开始用另一个名字来引用被称为fido的对象。 从现在开始, fido.colourthe_mangy_one.colour

那么, __init__里面的东西。 你可以把它们想象成狗的出生certificate。 colour本身是一个随机variables,可以包含任何东西。 fido.colourself.colour就像狗的身份self.colour表格一样; __init__是第一次填写的职员。

有什么更清楚的?

编辑 :扩展下面的评论:

你是指对象列表,不是吗?

首先, fido其实不是一个对象。 它是一个variables,它当前包含一个对象,就像当你说x = 5x是一个当前包含数字5的variables。 如果你后来改变主意,你可以做fido = Cat(4, "pleasing") (只要你创build了一个类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) 

这里, 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] 。 这些只是variables名称。 如果你想猫自己有名字,你必须为这个名字做一个单独的属性,然后覆盖__str__方法来返回这个名字。 这种方法(即类绑定函数,就像add__init__ )的目的是描述如何将对象转换为string,就像打印出来一样。

为我的5美分贡献给阿马丹的全面解释 。

在哪里类是一个抽象的“types”的描述。 物体是他们的实现:活的呼吸的东西。 在面向对象的世界中,主要的想法几乎可以称为一切的本质。 他们是:

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

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

  class Human: gender nationality favourite_drink core_characteristic favourite_beverage name age def love def drink def laugh def do_your_special_thing class Americans(Humans) def drink(beverage): if beverage != favourite_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_soccer_world_cup() class Germans(Humans) def drink(beverage): if favourite_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 

一些特征定义了人类。 但是每个国家都有所不同。 所以“民族型”也是一种有额外特征的人类。 “美国人”是一种“人”,从人类(基础阶级)inheritance了一些抽象的特征和行为:那就是inheritance。 所以所有的人都可以笑和喝,因此所有的孩子也可以! inheritance(2)。

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

所以每个人都有喜好,但每个国家都倾向于一种特殊的饮料。 如果你从人类的国籍中inheritance国籍,你可以覆盖inheritance的行为,如上面用drink()方法演示的那样。 但这仍然是在课堂上,正因为如此,这仍然是一个泛化。

 hans = German(favourite_drink = "Cola") 

实例化类德语,我在开始时“改变了”默认的特性。 (但是,如果你打电话给hans.drink('Milk'),他仍然会打印出“我需要更多的啤酒” – 这是一个明显的错误…或者如果我是一个更大的公司的员工,我会称之为function。 ;-)! )

例如Germans(hans)types的特征通常在实例化时通过构造函数(在python: __init__ )定义。 这是你定义一个类成为一个对象的点。 你可以通过填充个人特征并成为一个对象来将生命吸入抽象的概念(阶级)。

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

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

只是初始化实例的variables。

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

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

当你创build一个类的实例时,你可以给它一些初始的个性(状态或性格,如她的新生儿的着装和颜色)。 你用__init__做到这一点。

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

__init__函数设置类中的所有成员variables。 所以一旦你的bicluster创build,你可以访问该成员,并得到一个价值:

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

查看Python文档的一些信息。 你会想拿起一本关于面向对象概念的书继续学习。

跟随你的汽车的例子:当你得到一辆汽车,你只是不会得到一个随机的汽车,我的意思是,你select的颜色,品牌,座位数等,而有些东西也是“初始化”,没有你select因为它像轮子的数量或注册号码。

 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__方法中你定义了你正在创build的实例的属性。 所以,如果我们想要一辆蓝色的雷诺汽车,为2人,我们将初始化或实例的Car如:

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

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

  • 更多关于Python的类
  • 更多关于__init__方法

看起来你需要在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] 

上课狗(物):

 # Class Object Attribute species = 'mammal' def __init__(self,breed,name): self.breed = breed self.name = name 

在上面的例子中,我们使用物种作为一个全球性的,因为它会永远是相同的(你可以说的常量的种类)。当你调用init方法,那么init中的所有variables将被启动(例如:品种,名称)。

class Dog(object):a ='12'

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

如果你打印上面的例子,像这样调用下面

Dog.a 12

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

这意味着它只会在对象创build期间被初始化。 所以你想要声明为常量的任何东西都会使其成为全局variables,并且使用init来进行更改