Pourquoi utilisons-nous __init__ dans les classes Python?

124

J'ai du mal à comprendre l'initialisation des classes.

Quel est leur intérêt et comment savons-nous ce qu'il faut y inclure? L'écriture en classe nécessite-t-elle un type de réflexion différent de celui de créer des fonctions (je me suis dit que je pourrais simplement créer des fonctions, puis les envelopper dans une classe pour pouvoir les réutiliser. Est-ce que cela fonctionnera?)

Voici un exemple:

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

Ou un autre exemple de code:

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

Il y a tellement de classes que __init__je rencontre en essayant de lire le code d'autres personnes, mais je ne comprends pas la logique de leur création.

Âme perdue
la source
1
l'histoire d' init est ... bla, bla, bla .... constructeur-destructeur mais pas de destructeur car garbage collection disponible.
MisterGeeky

Réponses:

289

Par ce que vous avez écrit, il vous manque un élément essentiel de compréhension: la différence entre une classe et un objet. __init__n'initialise pas une classe, il initialise une instance d'une classe ou d'un objet. Chaque chien a une couleur, mais pas les chiens en tant que classe. Chaque chien a quatre pieds ou moins, mais pas la classe des chiens. La classe est un concept d'objet. Quand vous voyez Fido et Spot, vous reconnaissez leur similitude, leur dogness. Voilà la classe.

Quand tu dis

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

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

Vous dites que Fido est un chien brun avec 4 pattes tandis que Spot est un peu infirme et est principalement jaune. La __init__fonction s'appelle un constructeur, ou initialiseur, et est automatiquement appelée lorsque vous créez une nouvelle instance d'une classe. Dans cette fonction, l'objet nouvellement créé est affecté au paramètre self. La notation self.legsest un attribut appelé legsde l'objet dans la variable self. Les attributs sont un peu comme des variables, mais ils décrivent l'état d'un objet, ou des actions (fonctions) particulières disponibles pour l'objet.

Cependant, notez que vous ne vous fixez pas colourpour la doghood elle-même - c'est un concept abstrait. Il y a des attributs qui ont du sens sur les classes. Par exemple, en population_sizeest un - cela n'a pas de sens de compter le Fido parce que Fido en est toujours un. Il est logique de compter les chiens. Disons qu'il y a 200 millions de chiens dans le monde. C'est la propriété de la classe Dog. Fido n'a rien à voir avec le nombre de 200 millions, ni Spot. C'est ce qu'on appelle un "attribut de classe", par opposition aux "attributs d'instance" qui sont colourou legsplus.

Maintenant, à quelque chose de moins canin et de plus lié à la programmation. Comme j'écris ci-dessous, la classe pour ajouter des choses n'est pas raisonnable - de quoi s'agit-il? Les classes en Python sont constituées de collections de données différentes, qui se comportent de la même manière. La classe de chiens comprend Fido et Spot et 199999999998 autres animaux similaires à eux, tous urinant sur les lampadaires. En quoi consiste la classe pour ajouter des choses? Par quelles données inhérentes à eux diffèrent-ils? Et quelles actions partagent-ils?

Cependant, les chiffres ... ce sont des sujets plus intéressants. Dites, les nombres entiers. Il y en a beaucoup, beaucoup plus que des chiens. Je sais que Python a déjà des nombres entiers, mais jouons à l'idiot et les "implémentons" à nouveau (en trichant et en utilisant les entiers de Python).

Ainsi, les nombres entiers sont une classe. Ils ont des données (valeur), et certains comportements ("ajoutez-moi à cet autre nombre"). Montrons ceci:

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

C'est un peu fragile (nous supposons que ce othersera un MyInteger), mais nous l'ignorerons maintenant. Dans le vrai code, nous ne le ferions pas; nous le testerions pour nous en assurer, et peut-être même le forcerons ("vous n'êtes pas un entier? par golly, vous avez 10 nanosecondes pour en devenir un! 9 ... 8 ....")

On pourrait même définir des fractions. Les fractions savent également comment s'additionner.

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)

Il y a encore plus de fractions que d'entiers (pas vraiment, mais les ordinateurs ne le savent pas). Faisons deux:

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

Vous ne déclarez rien ici. Les attributs sont comme un nouveau type de variable. Les variables normales n'ont qu'une seule valeur. Disons que vous écrivez colour = "grey". Vous ne pouvez pas avoir une autre variable nommée colourqui n'est "fuchsia"pas au même endroit dans le code.

Les tableaux résolvent cela dans une certaine mesure. Si vous dites colour = ["grey", "fuchsia"], vous avez empilé deux couleurs dans la variable, mais vous les distinguez par leur position (0 ou 1, dans ce cas).

Les attributs sont des variables liées à un objet. Comme avec les tableaux, nous pouvons avoir beaucoup de colourvariables, sur différents chiens . Donc, fido.colourest une variable, mais spot.colourest une autre. Le premier est lié à l'objet dans la variable fido; le second spot,. Maintenant, quand tu appellesDog(4, "brown") , ou three.add(five), il y aura toujours un paramètre invisible, qui sera assigné au paramètre supplémentaire pendant au début de la liste de paramètres. Il est appelé conventionnellement selfet obtiendra la valeur de l'objet devant le point. Ainsi, dans le __init__(constructeur) du Chien , selfsera ce que le nouveau Chien se révélera être; dans MyInteger's add, selfsera lié à l'objet dans la variable three. Donc,three.valuesera la même variable en dehors du add, que self.valuedans le add.

Si je dis the_mangy_one = fido , je commencerai à faire référence à l'objet connu sous fidoun autre nom. Désormais, fido.colourc'est exactement la même variable que the_mangy_one.colour.

Alors, les choses à l'intérieur du __init__ . Vous pouvez les considérer comme notant des choses dans le certificat de naissance du chien. colouren soi est une variable aléatoire, peut contenir n'importe quoi. fido.colourou self.colourest comme un champ de formulaire sur la feuille d'identité du chien; et __init__le greffier le remplit-il pour la première fois.

Un plus clair?

EDIT : développer le commentaire ci-dessous:

Vous voulez dire une liste de objets , n'est-ce pas?

Tout d'abord, ce fidon'est en fait pas un objet. C'est une variable, qui contient actuellement un objet, tout comme quand vous dites x = 5, xest une variable contenant actuellement le nombre cinq. Si vous changez d'avis plus tard, vous pouvez le faire fido = Cat(4, "pleasing")(tant que vous avez créé une classeCat ), et à fidopartir de ce moment-là «contiendra» un objet chat. Si vous le faites fido = x, il contiendra alors le numéro cinq, et pas du tout un objet animal.

Une classe en elle-même ne connaît pas ses instances à moins que vous n'écriviez spécifiquement du code pour en garder une trace. Par exemple:

class Cat:
    census = [] #define census array

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

Voici censusun attribut de Catclasse au niveau de la classe.

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

Notez que vous n'obtiendrez pas [fluffy, sparky]. Ce ne sont que des noms variables. Si vous voulez que les chats eux-mêmes aient des noms, vous devez créer un attribut distinct pour le nom, puis remplacer la __str__méthode pour renvoyer ce nom. Le but de cette méthode (c'est-à-dire la fonction liée à une classe, tout comme addou __init__) est de décrire comment convertir l'objet en chaîne, comme lorsque vous l'imprimez.

Amadan
la source
7
wow merci .. cela a en fait beaucoup de sens pour moi donc tout ce qui fait quelque chose ce que c'est, je dois pré-déclarer dans la fonction init. Dans ce cas, le chien a des pattes et de la couleur. Par exemple, si je faisais une classe qui ajoutait deux nombres, je déclarerais self.firstnumber et self.secondnumber puis ferais simplement firstnumber + secondnumber plus tard dans la classe pour obtenir la réponse?
Lostsoul
1
En quelque sorte. Tu pourrais faire ça. Mais cela n'a guère de sens de créer une classe juste pour ajouter des choses. Les classes implémentent normalement des données avec des comportements - les comportements purs ne sont que des fonctions. Je vais développer la réponse avec quelque chose de pertinent; attends un peu.
Amadan
3
Merci pour la réponse incroyable. Je vois et je comprends maintenant le pouvoir des classes. Désolé, si cela semble stupide. Vous venez de me rendre compte que je peux trier les données et maintenir l'état de nombreuses choses différentes à la fois (alors que je ne suivrais que le nombre de variables que je peux créer ou plus via des boucles). Alors disons, j'ai besoin de calculer le nombre moyen de pattes par chien? Existe-t-il un moyen de récupérer une liste de tous les objets que j'ai créés avec une classe afin que je puisse démarrer une éducation comme celle-ci? ou devrais-je aussi maintenir une liste des classes que je crée (ie [fido, spot])
Lostsoul
23

Pour contribuer mes 5 cents à l' explication approfondie d'Amadan .

Où les classes sont une description "d'un type" d'une manière abstraite. Les objets sont leurs réalisations: la chose vivante qui respire. Dans le monde orienté objet, il y a des idées principales que vous pouvez presque appeler l'essence de tout. Elles sont:

  1. encapsulation (je ne m'étendrai pas là-dessus)
  2. héritage
  3. polymorphisme

Les objets ont une ou plusieurs caractéristiques (= Attributs) et comportements (= Méthodes). Le comportement dépend principalement des caractéristiques. Les classes définissent ce que le comportement doit accomplir d'une manière générale, mais tant que la classe n'est pas réalisée (instanciée) en tant qu'objet, elle reste un concept abstrait d'une possibilité. Permettez-moi d'illustrer à l'aide de «l'héritage» et du «polymorphisme».

    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

Certaines caractéristiques définissent les êtres humains. Mais chaque nationalité diffère quelque peu. Les "types nationaux" sont donc des humains avec des extras. Les "Américains" sont un type d '"Humains" et héritent de certaines caractéristiques et comportements abstraits du type humain (classe de base): c'est l'héritage. Ainsi, tous les humains peuvent rire et boire, donc toutes les classes enfants le peuvent aussi! Héritage (2).

Mais comme ils sont tous du même genre (Type / classe de base: Humains), vous pouvez les échanger parfois: voir la boucle for à la fin. Mais ils exposeront une caractéristique individuelle, et c'est le polymorphisme (3).

Ainsi, chaque humain a une boisson préférée, mais chaque nationalité a tendance à choisir un type de boisson spécial. Si vous sous-classez une nationalité du type Humains, vous pouvez remplacer le comportement hérité comme je l'ai démontré ci-dessus avec la drink()Méthode. Mais c'est toujours au niveau de la classe et à cause de cela, c'est toujours une généralisation.

hans = German(favorite_drink = "Cola")

instancie la classe German et j'ai "changé" une caractéristique par défaut au début. (Mais si vous appelez hans.drink ('Milk'), il imprimera toujours "J'ai besoin de plus de bière" - un bug évident ... ou peut-être que c'est ce que j'appellerais une fonctionnalité si je devais être un employé d'une plus grande entreprise. ;-)! )

Les caractéristiques d'un type par exemple les Allemands (hans) sont généralement définies via le constructeur (en python: __init__ au moment de l'instanciation. C'est le moment où vous définissez une classe pour devenir un objet. Vous pourriez dire insuffler la vie à un concept abstrait (classe) en le remplissant de caractéristiques individuelles et en devenant un objet.

Mais parce que chaque objet est une instance d'une classe, ils partagent tous certains types de caractéristiques de base et certains comportements. C'est un avantage majeur du concept orienté objet.

Pour protéger les caractéristiques de chaque objet, vous les encapsulez - cela signifie que vous essayez de coupler le comportement et les caractéristiques et qu'il est difficile de les manipuler de l'extérieur de l'objet. C'est l'encapsulation (1)

Don Question
la source
5

Il s'agit simplement d'initialiser les variables de l'instance.

Par exemple, créez une crawlerinstance avec un nom de base de données spécifique (à partir de votre exemple ci-dessus).

jldupont
la source
Je suis désolé, je ne comprends pas vraiment ce que cela signifie ... dans l'exemple ci-dessus ... le développeur n'aurait-il pas pu simplement ajouter dans son code principal «left = foo», etc.
Lostsoul
Vous voulez dire les valeurs par défaut de la fonction? left=Noneleft sera initialisé à Nonesi lors de la création le leftparamètre n'est pas spécifié.
jldupont
Je pense que cela commence à avoir du sens ... est-ce que c'est comme la façon dont vous devez prédéclarer vos variables en java "String left" ou quelque chose? puis une fois son initialisé à la classe, vous pouvez manipuler les valeurs? C'est juste un peu déroutant par rapport aux fonctions car je peux simplement envoyer des valeurs aux fonctions et je n'ai pas besoin d'initialiser quoi que ce soit à l'avance.
Lostsoul
1
@Lostsoul: left = foofonctionnerait - une fois. Le but des cours est de faire quelque chose de sensé pour chaque différent crawler. Les classes ne sont pas des fonctions, ni quelque chose qui peut se comparer à des fonctions (enfin, pas tant que vous n'êtes pas beaucoup plus avancé et que vous vous lancez dans la programmation fonctionnelle, mais cela ne fera que vous embrouiller maintenant). Lisez ma réponse pour savoir ce que sont réellement les cours - vous ne les comprenez toujours pas.
Amadan
4

Il semble que vous deviez l'utiliser __init__en Python si vous souhaitez initialiser correctement les attributs mutables de vos instances.

Consultez l'exemple suivant:

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

C'est assez différent en Java où chaque attribut est automatiquement initialisé avec une nouvelle valeur:

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

produit une sortie que nous attendons intuitivement:

[strange] []

Mais si vous déclarez attrcomme static, il agira comme Python:

[strange] [strange]
alexpirine
la source
3

Voici l' exemple de votre voiture : lorsque vous obtenez une voiture, vous n'obtenez tout simplement pas une voiture au hasard, je veux dire, vous choisissez la couleur, la marque, le nombre de sièges, etc. Et certaines choses sont également "initialiser" sans que vous choisissiez pour cela, comme le nombre de roues ou le numéro d'immatriculation.

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

Ainsi, dans la __init__méthode, vous définissez les attributs de l'instance que vous créez. Donc, si nous voulons une voiture Renault bleue, pour 2 personnes, nous initialiserions ou par exemple Car:

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

De cette façon, nous créons une instance de la Carclasse. C'est __init__celui qui gère nos attributs spécifiques (comme colorou brand) et qui génère les autres attributs, comme registration_number.

juliomalegria
la source
3

Les classes sont des objets avec des attributs (état, caractéristique) et des méthodes (fonctions, capacités) spécifiques à cet objet (comme la couleur blanche et les pouvoirs de mouche, respectivement, pour un canard).

Lorsque vous créez une instance d'une classe, vous pouvez lui donner une personnalité initiale (état ou caractère comme le nom et la couleur de sa robe pour un nouveau-né). Vous faites cela avec __init__.

En gros, __init__définit automatiquement les caractéristiques de l'instance lorsque vous appelez instance = MyClass(some_individual_traits).

Joaquin
la source
2

La __init__fonction configure toutes les variables membres de la classe. Ainsi, une fois votre bicluster créé, vous pouvez accéder au membre et récupérer une valeur:

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

Consultez la documentation Python pour plus d'informations. Vous voudrez prendre un livre sur les concepts OO pour continuer à apprendre.

AlG
la source
1
class Dog(object):

    # Class Object Attribute
    species = 'mammal'

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

Dans l'exemple ci-dessus, nous utilisons les espèces comme un global car il sera toujours le même (genre de constante que vous pouvez dire). lorsque vous appelez __init__method, toutes les variables à l'intérieur __init__seront initiées (par exemple: race, nom).

class Dog(object):
    a = '12'

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

si vous imprimez l'exemple ci-dessus en appelant ci-dessous comme ceci

Dog.a
12

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

Cela signifie qu'il ne sera initialisé que lors de la création de l'objet. donc tout ce que vous voulez déclarer comme constant le rend global et tout ce qui change utilise __init__

vedavyasa k
la source