150 votes

Pourquoi utilisons-nous __init__ dans les classes Python ?

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

Quel en est l'intérêt et comment savoir ce qu'il faut y inclure ? L'écriture dans des classes nécessite-t-elle un autre type de réflexion que la création de fonctions (je me suis dit que je pouvais simplement créer des fonctions et les envelopper dans une classe pour pouvoir les réutiliser. Cela fonctionnera-t-il ?)

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 avec __init__ Je les rencontre en essayant de lire le code d'autres personnes, mais je ne comprends pas la logique de leur création.

325voto

Amadan Points 41944

D'après 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 les chiens en tant que classe n'en ont pas. Chaque chien a quatre pattes ou moins, mais la classe des chiens n'en a pas. La classe est le concept d'un objet. Lorsque vous voyez Fido et Spot, vous reconnaissez leur similarité, leur caractère canin. C'est la classe.

Quand vous dites

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

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

Vous dites, Fido est un chien marron avec 4 pattes alors que Spot est un peu handicapé et est principalement jaune. Le site __init__ est appelée constructeur, ou initialisateur, 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.legs est un attribut appelé legs de 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 particulières (fonctions) disponibles pour l'objet.

Cependant, remarquez que vous ne mettez pas colour pour la cynophilie elle-même - c'est un concept abstrait. Il y a des attributs qui ont un sens sur les classes. Par exemple, population_size est l'un d'entre eux - cela n'a pas de sens de compter les Fido parce que Fido est toujours un. En revanche, 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 Chien. Fido n'a rien à voir avec le nombre 200 millions, pas plus que Spot. C'est ce qu'on appelle un "attribut de classe", par opposition aux "attributs d'instance" qui sont colour ou legs ci-dessus.

Passons maintenant à quelque chose de moins canin et de plus lié à la programmation. Comme je l'écris ci-dessous, une classe pour ajouter des choses n'est pas judicieuse - de quoi s'agit-il ? Les classes en Python sont constituées de collections de données différentes, qui se comportent de manière similaire. La classe des chiens est constituée de Fido, Spot et 199999999998 autres animaux similaires, qui pissent tous sur les lampadaires. De quoi se compose la classe des choses à ajouter ? Par quelles données qui leur sont inhérentes diffèrent-elles ? Et quelles actions ont-elles en commun ?

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

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

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

Ceci est un peu fragile (nous supposons que other sera un MyInteger), mais nous allons l'ignorer maintenant. Dans un code réel, nous ne le ferions pas ; nous le testerions pour nous en assurer, et peut-être même le contraindrions-nous ("vous n'êtes pas un entier ? parbleu, vous avez 10 nanosecondes pour le devenir ! 9... 8....")

On pourrait même définir les fractions. Les fractions savent aussi s'additionner entre elles.

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 même plus de fractions que d'entiers (pas vraiment, mais les ordinateurs ne le savent pas). Faisons-en 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 du tout 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 colour c'est-à-dire "fuchsia" - pas au même endroit dans le code.

Les tableaux résolvent ce problème 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 qui sont liées à un objet. Comme pour les tableaux, on peut avoir beaucoup de colour variables, sur des chiens différents . Donc, fido.colour est une variable, mais spot.colour en est une autre. Le premier est lié à l'objet dans la variable fido ; le second, spot . Maintenant, quand vous appelez Dog(4, "brown") ou three.add(five) il y aura toujours un paramètre invisible, qui sera assigné au paramètre supplémentaire en suspens au début de la liste des paramètres. Il est conventionnellement appelé self et obtiendra la valeur de l'objet situé devant le point. Ainsi, à l'intérieur de l'objet __init__ (constructeur), self sera ce que le nouveau chien sera ; dans le cadre de MyInteger 's add , self sera lié à l'objet dans la variable three . Ainsi, three.value sera la même variable en dehors de la add comme self.value au sein de la add .

Si je dis the_mangy_one = fido je vais commencer à me référer à l'objet connu sous le nom de fido avec un autre nom. A partir de maintenant, fido.colour est exactement la même variable que the_mangy_one.colour .

Donc, les choses à l'intérieur du __init__ . Vous pouvez les considérer comme des éléments à inscrire sur l'acte de naissance du chien. colour est en soi une variable aléatoire, qui peut contenir n'importe quoi. fido.colour ou self.colour est comme un champ de formulaire sur la fiche d'identité du chien ; et __init__ est le greffier qui le remplit pour la première fois.

C'est plus clair ?

EDIT : Développant le commentaire ci-dessous :

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

Tout d'abord, fido n'est en fait pas un objet. C'est une variable, qui contient actuellement un objet, tout comme lorsque vous dites x = 5 , x est une variable contenant actuellement le nombre cinq. Si vous changez d'avis plus tard, vous pouvez faire fido = Cat(4, "pleasing") (pour autant que vous ayez créé une classe Cat ), et fido contiendrait dorénavant un objet chat. Si vous faites fido = x il contiendra alors le chiffre cinq, et pas du tout un objet animal.

Une classe en elle-même ne connaît pas ses instances, sauf si vous écrivez spécifiquement du code pour en garder la trace. Par exemple :

class Cat:
    census = [] #define census array

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

Ici, census est un attribut de niveau classe de Cat 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 de variables. Si vous voulez que les chats eux-mêmes aient des noms, vous devez créer un attribut séparé pour le nom, et ensuite remplacer l'attribut __str__ pour retourner ce nom. La fonction de cette méthode (c'est-à-dire une fonction liée à une classe, tout comme la fonction add ou __init__ ) a pour but de décrire comment convertir l'objet en une chaîne de caractères, comme lorsque vous l'imprimez.

29voto

Don Question Points 3856

Pour apporter mes 5 centimes à la explication approfondie d'Amadan .

Les classes sont une description "d'un type" de 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 l'on peut presque appeler l'essence de tout. Il s'agit de :

  1. encapsulation (je ne m'étendrai pas sur ce sujet)
  2. héritage
  3. polymorphisme

Les objets ont une ou plusieurs caractéristiques (= attributs) et des comportements (= méthodes). Le comportement dépend principalement des caractéristiques. Les classes définissent ce que le comportement doit accomplir de 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 cela à 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 en quelque sorte 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 d'enfants le peuvent aussi ! Héritage (2).

Mais comme ils sont tous du même type (Type/classe de base : Humains), vous pouvez parfois les échanger : 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é tend vers un type de boisson particulier. Si vous sous-classez une nationalité à partir du type Humains, vous pouvez écraser le comportement hérité, comme je l'ai démontré ci-dessus avec l'option drink() Méthode. Mais c'est toujours au niveau de la classe et, de ce fait, 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 "I need more beer" - un bug évident ... ou peut-être que c'est ce que j'appellerais une caractéristique si j'étais employé d'une plus grande entreprise ;-) ! )

Les caractéristiques d'un type, par exemple les Allemands (hans), sont généralement définies par le biais du constructeur (en python : __init__ ) au moment de l'instanciation. C'est le moment où vous définissez une classe pour qu'elle devienne un objet. On pourrait dire que l'on donne vie à un concept abstrait (classe) en le remplissant de caractéristiques individuelles et en le transformant en objet.

Mais comme chaque objet est une instance d'une classe, ils partagent tous des caractéristiques-types de base et un certain comportement. C'est un avantage majeur du concept d'orientation objet.

Pour protéger les caractéristiques de chaque objet, vous les encapsulez - ce qui signifie que vous essayez de coupler le comportement et la caractéristique et de faire en sorte qu'il soit difficile de les manipuler depuis l'extérieur de l'objet. C'est l'encapsulation (1)

6voto

jldupont Points 31331

C'est juste pour initialiser les variables de l'instance.

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

5voto

juliomalegria Points 6281

En suivant avec votre voiture Par exemple, lorsque vous achetez une voiture, vous ne recevez pas une voiture au hasard, vous choisissez la couleur, la marque, le nombre de sièges, etc. Et certaines choses sont également "initialisées" sans que vous les choisissiez, 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 le __init__ vous définissez les attributs de l'instance que vous créez. Ainsi, si nous voulons une voiture Renault bleue, pour 2 personnes, nous initialiserons ou instance de Car comme :

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

De cette manière, nous créons une instance de l'élément Car classe. Le site __init__ est celui qui s'occupe de nos attributs spécifiques (comme le color ou brand ) et génère les autres attributs, comme registration_number .

5voto

alartur Points 379

Il semble que tu doives utiliser __init__ en Python si vous souhaitez initialiser correctement les attributs mutables de vos instances.

Voir 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 très 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 un résultat auquel nous nous attendons intuitivement :

[strange] []

Mais si vous déclarez attr como static il se comportera comme Python :

[strange] [strange]

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X