2009-06-28 7 views
1

premier message, alors jouez gentil!Python: liaison/liaison des variables ensemble dans un dictionnaire

J'ai une question relativement basique sur les dictionnaires Python. Je voudrais avoir une certaine valeur dict que les mises à jour lorsqu'une autre variable est modifiée (ou au moins recalculé quand il est ensuite appelé) - par exemple:

mass = 1.0 
volume = 0.5 
mydict = {'mass':mass,'volume':volume} 
mydict['density'] = mydict['mass']/mydict['volume'] 

Donc dans ce cas, mondict [ « densité »] retourne juste 2.0 . Si je change mydict ['mass'] = 2.0, la densité ne sera pas mise à jour. Fine - je peux comprendre pourquoi - la densité est définie par les valeurs quand elles ont été passées à la déclaration. Donc, je pensais que je pourrais aborder avec une expression lambda, par exemple (mes excuses pour le code horrible!):

mydict['density_calc'] = lambda x,y: x/y 
mydict['density'] = mydict['density_calc'](mydict['mass'],mydict['volume']) 

Mais encore une fois, cela ne renvoie la densité d'origine, malgré l'évolution mondict [ « masse »]. Comme une dernière tentative, j'ai essayé ceci:

def density(mass,volume): return mass/volume 
mydict['density_calc'] = lambda x,y: density(x,y) 
mydict['density'] = mydict['density_calc'](mydict['mass'],mydict['volume']) 

Encore une fois, pas de dés. Cela semble être un problème très simple à résoudre, alors je m'excuse d'avance, mais si quelqu'un pouvait m'aider, je serais très reconnaissant!

Cheers,

Dave

Répondre

-2

Voici un hack rapide sur la façon de sous-classe dict pour répondre à vos spécifications, mais probablement pas pour répondre aux spécifications de Python pour un dictionnaire:

class MyDict(dict): 
    def __getitem__(self, key): 
     if key == 'density': 
      return self['mass']/self['volume'] 
     else: 
      return dict.__getitem__(self,key) 
    def keys(self): 
     return ['density'] + dict.keys(self) 

x = MyDict() 
x['mass'] = 1.0 
x['volume'] = 0.5 

print x 

print x.keys() 

print x['density'] 

x['mass'] = 2.0 

print x['density'] 

qui imprime

{'volume': 0.5, 'mass': 1.0} 
['density', 'volume', 'mass'] 
2.0 
4.0 

Mais cela ne compte pour dict.iterkeys(), entre autres choses.

+2

Cela peut être plus proche de répondre à l'OP, mais c'est horrible. – FogleBird

+0

Merci à tous pour les réponses utiles. J'aurais peut-être dû être plus explicite au sujet de mes exigences. J'ai besoin de "porter" le dictionnaire - il est passé à travers beaucoup de code et dupliqué etc - de nombreuses variables sont calculés coûteux à calculer. J'espérais ne pas créer une nouvelle classe (je travaille avec python parallèle, ce que j'ai trouvé peut être un PITA pour manipuler des classes parfois) mais je pense que je vais utiliser cette solution. À votre santé! – Dave

+2

@Dave, vous devriez repenser votre parti pris contre les classes.Computationally cher est la raison pour laquelle nous utilisons des classes. Une classe devrait avoir du code qui évitera les calculs inutiles. Les attributs d'une classe sont déjà un dictionnaire. La surcharge d'un dictionnaire comme celui-ci viole les attentes des autres en cachant une clé «spéciale» différente de toutes les autres. –

3

Réponse courte est que vous ne pouvez pas.

L'expression affectée est évaluée strictly lorsqu'elle est rencontrée, donc toute modification ultérieure des termes importe peu.

Vous pouvez résoudre ce avec une classe dict personnalisée qui écrase le getitem et setitem méthodes et fait quelque chose de spécial (à savoir calcule la valeur) pour certaines des clés (par exemple la densité et un nombre fini d'autres).

class MyDict(dict): 
    def __getitem__(self, key): 
      if key == "density": 
        return self["mass"]/self["volume"] 
      return dict.__getitem__(self, key) 

d = MyDict() 
d["mass"] = 2.0 
d["volume"] = 4.0 
d["density"] # 0.5 

Regardez this.

9

Cela ressemble à un abus d'une structure de dictionnaire. Pourquoi ne pas créer une classe simple?

class Entity(object): 
    def __init__(self, mass, volume): 
     self.mass = mass 
     self.volume = volume 
    def _density(self): 
     return self.mass/self.volume 
    density = property(_density) 
4

Une classe serait faire mieux:

class Thing: 
    def __init__(self, mass, volume): 
     self._mass = mass 
     self._volume = volume 
     self._update_density() 

    def _update_density(self): 
     self._density = self._mass/self._volume 

    def get_density(self): 
     return self._density 

    density = property(get_density) 

    # You're unlikely to need these, but to demonstrate the point: 
    def set_mass(self, mass): 
     self._mass = mass 
     self._update_density() 

    def set_volume(self, volume): 
     self._volume = volume 
     self._update_density() 

brick = Thing(mass=2, volume=0.8) 
print brick.density 
# Prints 2.5 

brick.set_mass(4) 
print brick.density 
# Prints 5.0 

Je vous prends au mot que vous voulez la densité mis à jour lorsque vous définissez les autres valeurs - un moyen plus facile serait de calculer simplement à la volée quand il a demandé:

def get_density(self): 
     return self._mass/self._volume 

Ensuite, vous avez pas besoin _update_density() du tout.

+0

étrange que vous savez sur les propriétés, mais passer par toutes la peine de mettre à jour la densité chaque fois que quelque chose change. Optimisation prématurée? Trop de code. – FogleBird

+0

Oh, je viens de voir votre note en bas. Pourquoi ne pas le faire en premier lieu? – FogleBird

+0

J'ai répondu à la question posée, puis j'ai suggéré un moyen plus simple. Peut-être que Dave a une bonne raison de vouloir que la valeur soit mise à jour quand quelque chose change - comme si son vrai problème avait une valeur coûteuse à calculer ou quelque chose comme ça. – RichieHindle

6

Il est préférable de créer une classe dans ce cas et d'utiliser une propriété dynamique. .: par exemple

 

class Body(object): 
    def __init__(self): 
     self.mass=1.0 
     self.volume=0.5 

    @property 
    def density(self): 
     return self.mass/self.volume 
 

Cela vous donnera la masse, le volume et les propriétés de densité, où la densité est calculée sur la base des deux autres valeurs. par exemple.

 

b=Body() 
b.mass=1 
b.volume=0.5 
print b.density # should be 2 

b.mass=2.0 
print b.density # should be 4 
 

Toutefois, si vous êtes marié à l'aide d'un dictionnaire, vous devez étendre probablement et remplacer les __getitem__ et __setitem__ méthodes « magiques » à et détecter lorsque la masse a changé ou bien lorsque la densité est en cours d'accès et recalcule au besoin.

3

Le problème est que les dictionnaires ne sont pas le bon outil pour votre problème. Votre question est comme "comment puis-je enfoncer un clou avec un tournevis" ;-)

Les dictionnaires sont censés mapper les clés aux valeurs et ne se soucient pas des autres valeurs dans le dictionnaire. Ce que vous voulez est une classe

class PhysicalObject: 
    def __init__(self, mass, volume): 
     self.mass = float(mass) 
     self.volume = float(volume) 
    def setMass(self, mass): 
     self.mass = float(mass) 
    def setVolume(self, volume): 
     self.volume = float(volume) 
    def getDensity(self): 
     return self.mass/float(self.volume) 

v = PhysicalObject(1.0, 2.0) 
print v.getDensity() # prints 0.5 
v.setMass(2.0) 
v.setVolume(1.0) 
print v.getDensity() # prints 2.0 

Cet exemple recalcule la densité chaque fois que vous voulez obtenir, vous pouvez acheter aussi calculer dans les fonctions setMass et setVolume.

1

Ceci est si facile:

>>> mydict = {'mass':100, 'volume': 2, 'density': lambda: mydict['mass']/mydict['volume']} 
>>> mydict['density']() 
50 
>>> mydict['mass']=200 
>>> mydict['density']() 
100 
>>> mydict['volume'] = 4 
>>> mydict['density']() 
50 

Mes références lambda mondict et plus tard récupère les données les plus récentes tiennent dans mondict, donc me épargner la peine de passer la masse et le volume à la fonction chaque fois que je veux appeler, comme dans votre solution:

def density(mass,volume): return mass/volume 
mydict['density_calc'] = lambda x,y: density(x,y) 
mydict['density'] = mydict['density_calc'](mydict['mass'],mydict['volume']) 

Cependant mondict [ « densité »] est une fonction, pas la valeur elle-même.

+0

Hmm - Je pense que c'était proche de ce que je visais, mais je ne veux pas avoir à exécuter la fonction. Une grande partie de mon code repose sur les variables de mydict, et quand j'écris de nouvelles choses, c'est inévitable, j'oublie que certaines clés doivent être exécutées, d'autres peuvent juste être référencées. Merci pour la solution! – Dave

0

Un dictionnaire auto-référentiel correspondrait presque à votre deuxième tentative (il y a 7 ans) et fonctionnerait bien avec tout autre code utilisant ce dictionnaire. En d'autres termes, il ressemblera toujours et agira comme un canard.

class MyDict(dict): 
     def __getitem__(self,key): 
      value=dict.__getitem__(self,key)   
      if callable(value): 
       value=value(self) 
      return value 

mydict=MyDict({'mass':1.0,'volume':0.5}) 
mydict['density'] = lambda mydict: mydict['mass']/mydict['volume'] 

print(mydict['density']) 

2,0

mydict['mass']=2.0 
print(mydict['density']) 

4,0

Questions connexes