2017-10-15 10 views
1

Comment référencer une variable d'une instance dans un dict?Python: comment référencer une variable dans un dict?

ce code:

class Gold(): 
    def __init__(self): 
     self.amount = 10 

class Cheese(): 
    def __init__(self, gold): 
     self.gold = gold 
     self.wallet = {'gold' : self.gold.amount} 
     self.charge ={'gold' : 10} 

    def subtract(self): 
     for cat, cost in self.charge.iteritems(): 
      if self.wallet[cat] >= cost: 
       self.wallet[cat] -= cost 

gold = Gold() 
cheese = Cheese(gold) 
cheese.subtract() 
print 'gold amount =', cheese.gold.amount, 'wallet =', cheese.wallet 

rendements:

gold amount = 10 wallet = {'gold': 0} 

et non

gold amount = 0 wallet = {'gold': 0} 

dans le code actuel, je ne veux pas une liste complète des déclarations 'si'. donc je coupler un dict avec tous les coûts (frais) dans cet exemple à un dict avec toutes les valeurs réelles de sorte que je ne dois faire

if cost == 'gold': 
    pass 
if cost == 'action points': 
    pass 

etc

des conseils sur la façon de le faire? à la fin du quistion est:

a = 1 
b = a 

est-il possible de mettre à jour tous les deux a et b (b + = 1 jour seulement b à 2)

+2

Parce que la valeur dans le portefeuille * ne fait pas * référence à l'instance Gold, et les entiers sont immuables. – jonrsharpe

+1

pythontutor.com pourrait vous aider à comprendre cela. Vous ne référencez pas self.gold.amount dans votre dict. Il est copié là-dedans. – UpSampler

+0

C'est un site web très utile. Maintenant, je sais _why_ ça ne marche pas. peut-être des pointeurs dans la façon dont je _do_ référence le self.gold.amount? Merci beaucoup! –

Répondre

1

En Python:

  • toutes variables sont des références
  • entiers et les chaînes sont immuables

Alors, quand vous faites:

>>> a = 1 # a points to 1 
>>> b = a # b points to 1 
>>> b = 10 # b now points to 10, a still points to 1 and 1 is unchanged. 
>>> b 
10 

>>> a 
1 

Si vous utilisez un type de données mutable, il fonctionne:

>>> a = [] 
>>> b = a 
>>> b.append(10) 
>>> b 
[10] 

>>> a 
[10] 

>>> a[0] = 20 
>>> b 
[20] 

Peut-être que vous pouvez créer un conteneur générique:

class GenericContainer(object): 
    def __init__(self, **kwargs): 
     self._keys = set() 
     for k, v in kwargs.items(): 
      if k in ('inventory', 'charge'): 
       raise ValueError('Unable to override method {}'.format(k)) 
      setattr(self, k, v) 
      self._keys.add(k) 

    def charge(self, **kwargs): 
     for k, v in kwargs.items(): 
      if k in ('inventory', 'charge'): 
       raise ValueError('Unable to override method {}'.format(k)) 
      if v < 0 and getattr(self, k, 0) < abs(v): 
       raise ValueError("You don't have enough {}".format(k)) 
      setattr(self, k, getattr(self, k, 0) + v) 
      self._keys.add(k) 
     return self 

    def inventory(self): 
     return {k:getattr(self, k) for k in self._keys} 

    def __repr__(self): 
     return str(self.inventory()) 

Ensuite, votre portefeuille peut être une instance de GenericContainer :

>>> wallet = GenericContainer(gold=10, silver=5) 
>>> wallet 
{'gold': 10, 'silver': 5} 

>>> wallet.charge(gold=-5, silver=5) 
{'gold': 5, 'silver': 10} 

>>> wallet.charge(gold=-20) 
ValueError: You don't have enough gold 

>>> wallet.gold 
5 

Y ous pouvez définir et/ou récupérer les attributs directement:

>>> wallet.gold 
5 

>>> wallet.gold += 10 
>>> wallet.gold 
15 

>>> wallet 
{'gold': 15, 'silver': 10} 

Vous pouvez obtenir l'attribut par nom de l'inventaire ou en utilisant getattr:

>>> wallet.inventory().get('gold', 0) 
15 

>>> getattr(wallet, 'gold') 
15 

Vous pouvez définir par son nom en utilisant dict du paramètre: valeur ou setattr:

>>> wallet.charge(**{'gold': -10}) 
{'gold': 5, 'silver': 10} 

>>> setattr(wallet, 'gold', 20) 
>>> wallet.gold 
20 

en regardant le résultat, je vois pourquoi en Python j'utilise souvent juste un dictionnaire au lieu de créer une classe - même si je Ne manquez pas la syntaxe de l'objet Javascript où vous pouvez accéder aux propriétés en utilisant soit foo.bar ou foo["bar"].En Python, les structures de données intégrées sont très puissantes et les développeurs Python ont tendance à utiliser une approche «nous sommes tous des adultes consentants» où vous ne faites que transmettre des données sans trop vous soucier de la protection des attributs. Probablement je finirais faire ceci:

>>> wallet = {'gold': 10, 'silver': 15} 

Et au lieu d'utiliser une méthode charge(gold=10):

>>> wallet['gold'] += 10 

Il faut du temps pour ce « il est juste des données » approche de couler, mais après que vous rarement manquent les idiomes plus OO bureaucratiques si communs dans des langages comme Java ou C++ spécialement pour les petits projets/équipes.

+0

thx pour la réponse! pense que je vais utiliser la classe conteneur. Mais la question est toujours là je pense, (voir edit). mais merci de prendre le temps et d'être serviable! –

+0

m'a pris un certain temps pour comprendre votre réponse, mais il a résolu le problème. THX! –

1

Il semble que la manière la plus simple de faire ceci est de changer wallet d'un attribut à property.

class Cheese(): 
    def __init__(self, gold): 
     self.gold = gold 
     self.charge ={'gold' : 10} 

    @property 
    def wallet(self): 
     return {'gold': self.gold.amount) 

    def subtract(self): 
     for cat, cost in self.charge.iteritems(): 
      if self.wallet[cat] >= cost: 
       self.wallet[cat] -= cost 

Les propriétés sont des méthodes qui acte comme attributs. Ils sont calculés sur appel à chaque fois plutôt que calculé lorsque l'objet est créé et plus jamais. Vous les utilisez de la même manière, par exemple:

>>> print 'gold amount =', cheese.gold.amount, 'wallet =', cheese.wallet 
gold amount = 0 wallet = {'gold': 0} 
+0

bien que je me demande pourquoi 'Gold' a besoin de sa propre classe ... –

+0

le code que vous avez écrit me donne: quantité d'or = 10 wallet = {'gold': 10}. commencera à lire sur les propriétés bien que –

+0

était un exemple. Le code actuel a beaucoup plus de lignes et des noms incompréhensibles –