IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

Probleme d'utilisation du generator "for"


Sujet :

Python

  1. #1
    Membre régulier
    Inscrit en
    Septembre 2004
    Messages
    187
    Détails du profil
    Informations forums :
    Inscription : Septembre 2004
    Messages : 187
    Points : 97
    Points
    97
    Par défaut Probleme d'utilisation du generator "for"
    Bonjour à tous,
    Quelle différence(s) entre mes classes obj et Coord empêche(nt) d'utiliser le generator "for" comme il se doit?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    #!/usr/bin/python
    # *-* coding:UTF-8 *-*
     
    class Coord(tuple):
    	def __new__(cls,*arg):
    		r=tuple.__new__(cls,arg[0]) if len(arg)==1 else tuple.__new__(cls,arg)
    		return r
    	def __init__(self,*arg):
    		self.__coord=arg[0] if len(arg)==1 else list(arg)
    		self.__dim=len(self.coord)
            @property
    	def coord(self):
    		return self.__coord
            @property
    	def dim(self):
    		return self.__dim
            @coord.setter
    	def coord(self,add):
    		print "Erreur!!"
            @coord.deleter
    	def coord(self):
    		print "Erreur!!"
     
    	#Surcharges
    	def __str__(self):
    		return 'Coord'+str(tuple(self.coord))
    	def __hash__(self):
    		v=0
    		pw=self.dim-1
    		for c in self.__coord:
    			v+=c*10**pw
    			pw-=1
    		return v
    	def __add__(self,other):
    		if not isinstance(other,Coord):
    			print "Erreur!!"
    		else:
    			gt=self if self.dim >= other.dim else other
    			lt=other if other.dim <= self.dim else self
    #			print ("gt=%s, lt=%s" % (gt,lt))
    			ad=gt.dim-lt.dim
    			val=()
    			resized=lt.coord
    			if ad!=0:
    				for c in xrange(ad):
    					resized+=0,
    			for v1,v2 in zip(gt.coord,resized):
    				val+=v1+v2,
     
    			return Coord(val)
    	def __sub__(self,other):
    		if not isinstance(other,Coord):
    			print "Erreur!!"
    		else:	
    			gt=self if self.dim >= other.dim else other
    			lt=other if other.dim <= self.dim else self
    			fct=-1 if other.dim>self.dim else 1
    			ad=gt.dim-lt.dim
    			val=()
    			resized=lt.coord
    			if ad!=0:
    				for c in xrange(ad):
    					resized+=0,
    			for v1,v2 in zip(gt.coord,resized):
    				val+=(v1-v2)*fct,
     
    			return Coord(val)
    	def __mul__(self,other):
    		if not isinstance(other,Coord):
    			print "Erreur!!"
    		else:
    			gt=self if self.dim >= other.dim else other
    			lt=other if other.dim <= self.dim else self
    			ad=gt.dim-lt.dim
    			val=()
    			resized=lt.coord
    			if ad!=0:
    				for c in xrange(ad):
    					resized+=0,
    			for v1,v2 in zip(gt.coord,resized):
    				val+=v1*v2,
     
    			return Coord(val)
    	def __eq__(self,other):
    		if not isinstance(other,Coord):
    			print "Erreur!"
    		else:
    			return self.coord==other.coord
    	def __ne__(self,other):
    		if not isinstance(other,Coord):
    			print "Erreur!"
    		else:
    			return self.coord!=other.coord
    	def __gt__(self,other):
    		if not isinstance(other,Coord):
    			print "Erreur!!"
    		else:
    			return self.__hash__()>other.__hash__()
    	def __ge__(self,other):
    		if not isinstance(other,Coord):
    			print "Erreur!!"
    		else:
    			return self.__hash__()>=other.__hash__()
    	def __lt__(self,other):
    		if not isinstance(other,Coord):
    			print "Erreur!!"
    		else:
    			return self.__hash__()<other.__hash__()
    	def __le__(self,other):
    		if not isinstance(other,Coord):
    			print "Erreur!!"
    		else:
    			return self.__hash__()<=other.__hash__()
     
    def step(c_min,c_max):
    	#TODO test de validite des arguments
     
    	t_min,t_max=list(c_min.coord),list(c_max.coord)
    	l_min,l_max,ret_list,mods=[],[],[],[]
    	for v1,v2 in zip(t_min,t_max):
    		l_min.append(min([v1,v2]))
    		ret_list.append(min([v1,v2]))
    		l_max.append(max([v1,v2]))
     
    #	print "l_min-->",l_min,"\nl_max-->",l_max,"\nret_list-->",ret_list
     
    	max_click,count=1,1
     
    	for mn,mx in zip(l_min,l_max):
    		mods.append(abs(max_click))
    		max_click*=abs(mx-mn)+1
     
    #	print "max_click-->",max_click,"\nmods-->",mods
    	while count<=max_click:
     
    		for i in xrange(len(l_min)):
    			if not count%mods[i]:
    				#print ("not %d%%%d = %s" % (count,mods[i],not count%mods[i]))
    				ret_list[i]=ret_list[i]+1 if ret_list[i]<l_max[i] else l_min[i]
    				#print ("ret_list[%d]<l_max[%d] = %s  l_min[%d] = %s" % (i,i,ret_list[i]<l_max[i],i,l_min[i]))
    				#print ("ret_list[%d] = %s" % (i,ret_list[i]))
     
    		yield Coord(ret_list)
    		count+=1
     
    class obj(object):
    	oid=1
    	def __init__(self):
    		self.oid=obj.oid
    		obj.oid+=1
    	def __str__(self):
    		return ("obj.%d" % self.oid)
    	def __repr__(self):
    		return ("obj.%d" % self.oid)
    	def __hash__(self):
    		return self.oid
     
    def robj(mx):
    	count=1
    	while count<=mx:
    		yield obj()
    		count+=1
     
    class mini_field():
    	def __init__(self):
    		self.__d={}
    		for c in step(Coord(1,1),Coord(1,4)):
    			self.__d[c]=c
    		for o in robj(4):				
    			self.__d[o]=o
            @property
    	def d(self):
    		return self.__d
     
    if __name__=='__main__':
    	f=mini_field()
    	for k in f.d:
    		print k,f.d[k]
    	print f.d.keys()
    Voici ce que j'obtiens en sortie:
    obj.1 obj.1
    obj.2 obj.2
    obj.3 obj.3
    obj.4 obj.4
    Coord(1, 1) Coord(1, 1)
    Coord(1, 1) Coord(1, 1)
    Coord(1, 1) Coord(1, 1)
    Coord(1, 1) Coord(1, 1)
    [obj.1, obj.2, obj.3, obj.4, (1, 1), (1, 2), (1, 3), (1, 4)]
    Je ne retrouve plus les clés de type Coord en passant par un generator...

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Salut,

    Lorsque vous faites print f.d.keys() çà affiche la liste f.d.keys()...
    Normal: on applique str sur la liste.
    Vous souhaiteriez que le str s'applique aux éléments de la liste...
    Ca ne fonctionne pas comme çà, snif?

    Pour vous en convaincre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    >>class A:
    >>    def __str__(self): return 'A'
    >> li = [ 1, 2, 3, A() ]
    >> print li
    [1, 2, 3, <__main__.A instance at 0x28c8c8>]
    >>> print ', '.join(str(e) for e in li)
    1, 2, 3, A
    -W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  3. #3
    Membre régulier
    Inscrit en
    Septembre 2004
    Messages
    187
    Détails du profil
    Informations forums :
    Inscription : Septembre 2004
    Messages : 187
    Points : 97
    Points
    97
    Par défaut
    Bonsoir,
    Les problèmes de mise en forme ne me dérange guère. Ce que je trouve beaucoup plus gênant c'est:
    qui a pour réponse:
    Traceback (most recent call last):
    File "./Coord_beta.py", line 180, in <module>
    print f.d[Coord(1,2)]
    KeyError: (1, 2)
    Alors même que la clé est présente dans la liste f.d.keys()!!

  4. #4
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Re...

    Citation Envoyé par Krishna Voir le message
    Bonsoir,
    Les problèmes de mise en forme ne me dérange guère.
    N'étaient-ils pas la raison du premier post?

    Ce que je trouve beaucoup plus gênant c'est:
    qui a pour réponse:
    Alors même que la clé est présente dans la liste f.d.keys()!!
    Heu... comme vous ne dites pas pourquoi çà vous gêne, vous laissez penser que vous considérez "Coord(1,2)" comme une chaine de caractère et non comme une nouvelle, à priori, instance de Coord(1,2)...

    Qu'avez vous fait pour que çà puisse fonctionner?

    Bon d'accord en prenant le temps de fouiller le code on trouve une fonction __hash__ qui pourrait, si complétée par __cmp__, le faire...

    Mais c'est beurk: retournez l'instance précédemment créé via __new__ plutôt que de multiplier des objets identiques mais pas vraiement pareil...
    Vous vous entrainez à coder toxique ou quoi?
    - W
    PS: A quoi sert d'avoir sous classé tuple si c'est pour stocker les valeurs 'ailleurs' et avoir une fonction de hash en plus???

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class Coord(tuple):
        _instances_ = {}
        def __new__(cls, *args):
            if args not in cls._instances_:
                cls._instances_[args] = tuple.__new__(cls, args)
            return cls._instances_[args]
     
    t = Coord(1, 1)
    v = Coord(1, 1)
    print 't == v: %s' % (t == v) # t et v sont égaux
    print 'id(t) = %s' % id(t) # mais aussi identiques
    print 'id(v) = %s' % id(v)
     
    dd = {}                         # si nous mettons cela dans un dict..
    for x in range(4):
        c = Coord(1, x)
        dd[c] = c
     
    if t in dd: print 'known'  # pas besoin de __cmp__ ou de __hash__
    print dd[Coord(1,1)]
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  5. #5
    Membre régulier
    Inscrit en
    Septembre 2004
    Messages
    187
    Détails du profil
    Informations forums :
    Inscription : Septembre 2004
    Messages : 187
    Points : 97
    Points
    97
    Par défaut
    Bonsoir, re...bonsoir...

    L'utilisation du generator "for" me permettrait avant tout d'accéder au contenu du dictionnaire.
    Certes, le manque de clarté dans ma question permet toutes les interprétations...

    Qu'avez vous fait pour que çà puisse fonctionner?

    Bon d'accord en prenant le temps de fouiller le code on trouve une fonction __hash__ qui pourrait, si complétée par __cmp__, le faire...
    Je n'ai rien lu jusqu'ici sur __cmp__ dans mon livre de référence python. Je vais m'y employer de ce pas et corriger le code en conséquence.

    Mais c'est beurk: retournez l'instance précédemment créé via __new__ plutôt que de multiplier des objets identiques mais pas vraiement pareil...
    Alors là j'aurais vraiment besoin de précision. Est-ce une question d'espace mémoire? Comment fait-on pour retourner l'instance précédemment créée via __new__?

    Vous vous entrainez à coder toxique ou quoi?
    Qu'est-ce que du code toxique?

  6. #6
    Membre éprouvé
    Homme Profil pro
    Inscrit en
    Décembre 2007
    Messages
    758
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations professionnelles :
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Décembre 2007
    Messages : 758
    Points : 970
    Points
    970
    Par défaut
    Citation Envoyé par Krishna Voir le message
    Comment fait-on pour retourner l'instance précédemment créée via __new__?
    bonsoir,

    c'est le code qu'a mis wiztricks dans son dernier message

  7. #7
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Points : 1 384
    Points
    1 384
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    retournez l'instance précédemment créé via __new__ plutôt que de multiplier des objets identiques mais pas vraiement pareil...
    Ce qui m'a toujours dérangé avec cette technique est que la méthode __init__ est quand-même appelée, même si on retourne un objet déjà construit...

  8. #8
    Membre régulier
    Inscrit en
    Septembre 2004
    Messages
    187
    Détails du profil
    Informations forums :
    Inscription : Septembre 2004
    Messages : 187
    Points : 97
    Points
    97
    Par défaut

    La classe...
    devidee,
    Peux-tu m'exposer brièvement (même si je considère mon post comme résolu) dans quelles circonstances cette technique peut poser problème?

  9. #9
    Membre éprouvé
    Homme Profil pro
    Inscrit en
    Décembre 2007
    Messages
    758
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations professionnelles :
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Décembre 2007
    Messages : 758
    Points : 970
    Points
    970
    Par défaut
    ce que veut dire dividee, c'est que lorsque __new__ renvoie une instance qui existe déjà celle-ci repasse par la case "__init__". Tout le travail qui avait été fait lors de la création de cette instance est alors fait à nouveau (mais à l'identique). Quelque part, c'est une perte de temps (surtout si le travail fait par __init__ est long).

  10. #10
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut ajout delete
    Salut,
    Ce qui m'a toujours dérangé avec cette technique est que la méthode __init__ est quand-même appelée, même si on retourne un objet déjà construit...
    Intellectuellement, je suis d'accord: c'est pas top.
    Mais la chose permet de "tuner" ce qu'on fait dans __init__ ou dans __new__ jusqu'à supprimer complètement __init__.
    Les vrais problèmes se posent lorsqu'on sous classe ce type d'objet mais c'est déjà le cas pour toutes les classes qui comportent une partie statique.
    Voir les DP Singleton et FlyWeight - ce dernier étant cela.

    Un autre problème est la gestion du delete ou le contrôle du nombre d'individu de la collection.

    Python utilise ce genre de technique pour mémoriser les objets de type int et les float. L'idée étant qu'il n'est pas utile de les détruire... Suivant l'algo. leur quantité devient insupportable côté occupation mémoire: elle n'est libérée qu'à la sortie du programme.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  11. #11
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Citation Envoyé par kango Voir le message
    ce que veut dire dividee, c'est que lorsque __new__ renvoie une instance qui existe déjà celle-ci repasse par la case "__init__". Tout le travail qui avait été fait lors de la création de cette instance est alors fait à nouveau (mais à l'identique). Quelque part, c'est une perte de temps (surtout si le travail fait par __init__ est long).
    La chose permet de définir une collection d'objets ayant un identifiant qui sert de clé: chaque instance ne doit être initialisée qu'une seule fois 'à la création' et nécessairement dans/via __new__.
    Pourquoi déclarer __init__ dans ce cas?
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  12. #12
    Membre éprouvé
    Homme Profil pro
    Inscrit en
    Décembre 2007
    Messages
    758
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations professionnelles :
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Décembre 2007
    Messages : 758
    Points : 970
    Points
    970
    Par défaut
    c'est pas faux, rien ne nous empêche de ne PAS implémenter __init__

  13. #13
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut toxique!
    Salut,
    Il n'est pas toujours facile de mettre du verbe sur une réaction épidermique
    Mais Coord sous classe tuple en stockant le vecteur passé en paramètre dans une liste (et dans self via le tuple.__new__).
    Une des différences entre tuple et list est la mutabilité du second que vous traitez bizarrement car __add__ et __sub__ retourne de nouveaux Coord i.e. l'objet est 'immutable' même si on utilise les valeurs du vecteur de la liste __coord.
    In fine, tout cela manque de cohérence et la vraie question est de savoir ce que vous voulez vraiment faire i.e. pour représenter vos vecteurs optez vous de sous classer list ou de sous classer tuple?
    La mutabilité en découle... et la nécessité d'écrire __hash__ et __cmp__ sans doute aussi.

    Pour l'instant, le seul attribut ajouté à la classe de base est 'dim', la longueur qui peut être calculée dynamiquement et vu comme attribut grace à property. Ce qui laisse penser que les vecteurs (Coord) sont des list ou des tuple auxquels sont ajoutés les methods/opérations __add__, __sub__, ...
    qui retourne ou pas une nouvelle instance (list donne le choix, tuple: non).
    Et si nous accrochons que des méthodes à un type de base immutables, pourquoi mémoriser les objets crées?
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  14. #14
    Membre extrêmement actif
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Points : 1 658
    Points
    1 658
    Par défaut
    Citation Envoyé par dividee Voir le message
    Ce qui m'a toujours dérangé avec cette technique est que la méthode __init__ est quand-même appelée, même si on retourne un objet déjà construit...


    Citation Envoyé par kango Voir le message
    ce que veut dire dividee, c'est que lorsque __new__ renvoie une instance qui existe déjà celle-ci repasse par la case "__init__". Tout le travail qui avait été fait lors de la création de cette instance est alors fait à nouveau (mais à l'identique). Quelque part, c'est une perte de temps (surtout si le travail fait par __init__ est long).

    Et si on fait ? :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class Coord(tuple):
        _instances_ = {}
        def __new__(cls, *args):
            if args not in cls._instances_:
                cls._instances_[args] = tuple.__new__(cls, args)
                return cls._instances_[args]
            else:
                return Coord._instances_[args]




    PS



    Dans le code de wiztricks (message #4), il n’apparait pas __init__()

    Si ça avait été le cas, j’aurais bien placé un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    print ’quelque chose de visible’
    dans cet __init__() pour me convaincre de visu que le programme passe dans __init__() dans tous les cas.

    Quel code écrire pour obtenir une telle confirmation ?

  15. #15
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Bonjour Eyquem

    Citation Envoyé par eyquem Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class Coord(tuple):
        _instances_ = {}
        def __new__(cls, *args):
            if args not in cls._instances_:
                cls._instances_[args] = tuple.__new__(cls, args)
                return cls._instances_[args]
            else:
                return Coord._instances_[args]
    cls._instances_ est la même chose que Coord._instances_ donc:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class Coord(tuple):
        _instances_ = {}
        def __new__(cls, *args):
            if args not in cls._instances_:
                cls._instances_[args] = tuple.__new__(cls, args)
            return cls._instances_[args]
    me paraît suffisant.
    Pour le reste, il est tellement 'simple' d'ajouter:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
        def __init__(self, *args):
            print 'coucou'
    que je n'ai sans doute pas compris la question.
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  16. #16
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Points : 1 384
    Points
    1 384
    Par défaut
    Citation Envoyé par kango Voir le message
    ce que veut dire dividee, c'est que lorsque __new__ renvoie une instance qui existe déjà celle-ci repasse par la case "__init__". Tout le travail qui avait été fait lors de la création de cette instance est alors fait à nouveau (mais à l'identique). Quelque part, c'est une perte de temps (surtout si le travail fait par __init__ est long).
    Une perte de temps c'est le cas "gentil". Cela peut aussi faire échouer __init__, augmenter l'utilisation mémoire qu'on essayait de limiter, ou même donner un comportement incorrect si on n'y prend pas garde.

    Citation Envoyé par wiztricks Voir le message
    La chose permet de définir une collection d'objets ayant un identifiant qui sert de clé: chaque instance ne doit être initialisée qu'une seule fois 'à la création' et nécessairement dans/via __new__.
    Pourquoi déclarer __init__ dans ce cas?
    - W
    En cas d'héritage, c'est le __init__ de la classe parente qui sera appelé (dans le cas présent, tuple.__init__).
    Il faudrait donc ajouter:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
        def __init__(self, *args):
            pass
    pour l'éviter.

  17. #17
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Bonjour Dividee,

    Je ne sais pas s'il faut s'attacher à ce genre de détails car ce qui doit être fait dans __new__ et dans __init__ est à traiter au cas par cas.
    Ici, dans cet exemple, nous sous-classons un type de base: je ne suis pas certain qu'une méthode __init__ soit définie pour les "tuple".

    En cas d'héritage, c'est le __init__ de la classe parente qui sera appelé (dans le cas présent, tuple.__init__).
    Il faudrait donc ajouter:
    ??? Si la surclasse définit une méthode '__init__' il faudra l'appeler au moins dans __new__ après la création de l'instance... ou via __init__ en lui passant les paramètres attendus.
    Dans ce cas, on peut être embêté pour définir le passage de paramètre de __new__ à __init__:
    - on pose que c'est __init__ qui insère l'instance dans le __dict__,
    - on ajoute **kwds au paramètres et __new__ positionne kwds['do_init'] à True
    - autres,
    Les règles de 'compositions' ne sont pas (voire jamais) simples.
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  18. #18
    Membre éclairé
    Homme Profil pro
    heu...
    Inscrit en
    Octobre 2007
    Messages
    648
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : heu...

    Informations forums :
    Inscription : Octobre 2007
    Messages : 648
    Points : 773
    Points
    773
    Par défaut
    Hmmm... je lis ce sujet avec interêt, mais une situation me turlupine... :
    imaginons que nous devions faire un traitement sur un dictionnaire avec des centaines de millions (ou millirads ou plus, bref énormément) de clés qui chacune est une instance de Coord, mais qu'ensuite, nous passions à un tout autre traitement qui n'a plus besoin du dictionnaire ou des instances de Coord, Coord._instances_ garderait une référence des différentes instances, qu'il faudrait manuellement supprimer pour que cela n'encombre plus la mémoire...

    Dans ce cas, l'implémentation de __cmp__() (ou __eq__()) et __hash__() ne serait-elle pas plus judicieuse ? Et si oui, auriez vous un exemple (oui, j'ai pas capté grand chose à __hash__()... ) ? Et si non, pourquoi (ou pourquoi ce serait 'beurk' )?

  19. #19
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Points : 1 384
    Points
    1 384
    Par défaut
    Ici, dans cet exemple, nous sous-classons un type de base: je ne suis pas certain qu'une méthode __init__ soit définie pour les "tuple".
    dir(tuple) m'indique que tuple possède bien une méthode __init__.

    ??? Si la surclasse définit une méthode '__init__' il faudra l'appeler au moins dans __new__ après la création de l'instance... ou via __init__ en lui passant les paramètres attendus.
    Je ne comprends pas les trois points d'interrogation. Oui, il faudra certainement appeler la méthode __init__ du parent dans __new__, mais il faudra aussi éviter qu'elle soit appelée automatiquement (une deuxième fois donc!) par le mécanisme de création d'instances.

    Un exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class A(object):
        def __init__(self, *args):
            print "A.__init__"
     
    class B(A):
        _instances_ = {}
        def __new__(cls, *args):
            if args not in cls._instances_:
                self = A.__new__(cls, *args)
                A.__init__(self, *args)
                cls._instances_[args] = self
            return cls._instances_[args]
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    >>> b = B(42)
    A.__init__
    A.__init__ # de trop
    >>> c = B(42)
    A.__init__ # de trop, retourne la même instance que b
    D'où la nécessité de définir une méthode __init__ vide dans la classe B pour éviter cela.

    Dans ce cas, on peut être embêté pour définir le passage de paramètre de __new__ à __init__:
    - on pose que c'est __init__ qui insère l'instance dans le __dict__,
    - on ajoute **kwds au paramètres et __new__ positionne kwds['do_init'] à True
    - autres,
    Les règles de 'compositions' ne sont pas (voire jamais) simples.
    - W
    D'accord, on ne peut pas donner de solution générale, mais ce n'était pas mon propos, je voulais juste attirer l'attention sur le point ci-dessus qu'il est facile d'oublier...

  20. #20
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Salut N.tox

    J'ai déjà mentionné les soucis que tu énonces.
    Comme dans le cas précédent, il faut rester "pragmatique".

    Citation Envoyé par N.tox Voir le message
    imaginons que nous devions faire un traitement sur un dictionnaire avec des centaines de millions (ou millirads ou plus, bref énormément) de clés qui chacune est une instance de Coord, mais qu'ensuite, nous passions à un tout autre traitement qui n'a plus besoin du dictionnaire ou des instances de Coord, Coord._instances_ garderait une référence des différentes instances, qu'il faudrait manuellement supprimer pour que cela n'encombre plus la mémoire...
    Ah ben le problème de la destruction est un des soucis de la chose et les types int et float présentent ce problème.
    Une solution pourrait être de déclarer le dico _instance_ weakref.
    Après tout, s'il n'y a plus de références à un objet dont la clé est X...

    Autres applications: la chose pourrait être le cache d'une partie des millions d'enregistrements d'une base de donnée. Dans ce cas, nous aurions à définir la 'clé' qui permet de savoir si une copie mémoire de l'enregistrement existe déjà ou pas.

    Dans ce cas, l'implémentation de __cmp__() (ou __eq__()) et __hash__() ne serait-elle pas plus judicieuse ? Et si oui, auriez vous un exemple (oui, j'ai pas capté grand chose à __hash__()... ) ? Et si non, pourquoi (ou pourquoi ce serait 'beurk' )?
    C'est "beurk" dans le contexte clé => attributs.
    Prenons des "tuple":
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    a = 1, 2
    b = 1, 2
    c = 3
    assert a == b
    assert c != a
    assert id(a) != id(b)
    Note: contrairement aux "int", deux "tuple" égaux n'ont pas la même adresse.
    Sous classons:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class T(tuple): pass
    a = T((1, 2))
    b = T((1, 2))
    assert a == b
    assert id(a) != id(b)
    Pas de changement, ouf... mais ajoutons de la viande à T:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class T(tuple):
        count = 0
     
        def __init__(self, *args):
            self._count = T.count
            T.count += 1
    Les relations précédentes sont toujours valables : T dérive de tuple.
    Et la on est embêté... car si nos objets égaux ne sont plus si égaux que çà...
    __hash__, __cmp__ les 'trouvent' égaux:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    >>> dd = {}
    >>> dd[a] = a
    >>> print dd[b]._count
    0
    alors que nous devrions avoir 1...

    La relation d'identité ainsi déconstruite difficile de définir des formes.
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Réponses: 2
    Dernier message: 30/08/2004, 14h48

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo