59 votes

Comment et quand utiliser à bon escient fairef en Python

J'ai un code où les instances de classes ont un parent<->enfant références les uns aux autres, par exemple:

class Node(object):
  def __init__(self):
    self.parent = None
    self.children = {}
  def AddChild(self, name, child):
    child.parent = self
    self.children[name] = child

def Run():
  root, c1, c2 = Node(), Node(), Node()
  root.AddChild("first", c1)
  root.AddChild("second", c2)
Run()

Je pense que cela crée des références circulaires telles que root, c1 et c2 ne sera pas libéré après Run() est terminée, non?. Alors, comment les amener à être libéré? Je pense que je peux faire quelque chose comme root.children.clear()ou self.parent = None - mais si je ne sais pas quand le faire?

Est-ce un moment approprié pour utiliser le weakref module? Ce qui, exactement, je ne weakref'ify? l' parent d'attribut? L' children d'attribut? L'ensemble de l'objet? Tous les ci-dessus? Je vois parler de la WeakKeyDictionary et weakref.proxy, mais il n'est pas clair pour moi comment ils doivent être utilisés, le cas échéant, dans ce cas.

C'est aussi sur python2.4 (pouvez pas mettre à niveau).

Mise à jour: Exemple et Résumé

Quels sont les objets à weakref-identifier dépend de l'objet qui peut vivre sans l'autre, et quels sont les objets dépendent les uns des autres. L'objet qui vie la plus longue doit contenir weakrefs à la courte durée de vie des objets. De même, weakrefs ne doit pas être faite à ses dépendances, - si elles le sont, la dépendance pourrait silencieusement disparaître, même si c'est encore nécessaire.

Si, par exemple, vous avez une structure en arbre, root, qui a des enfants, kids, mais peut exister sans les enfants, puis l' root objet doit utiliser weakrefs pour son kids. C'est également le cas si l'enfant de l'objet dépend de l'existence de l'objet parent. Ci-dessous, l'objet enfant nécessite un parent afin de calculer la profondeur, d'où la forte-ref pour parent. Les membres de l' kids attribut est facultatif, cependant, de sorte weakrefs sont utilisés pour prévenir une référence circulaire.

class Node:
  def __init__(self)
    self.parent = None
    self.kids = weakref.WeakValueDictionary()
  def GetDepth(self):
    root, depth = self, 0
    while root:
      depth += 1
      root = root.parent
    return count
root = Node()
root.kids["one"] = Node()
root.kids["two"] = Node()
# do what you will with root or sub-trees of it.

Pour inverser la relation autour de nous, nous avons quelque chose comme ci-dessous. Ici, l' Facade classes nécessitent Subsystem exemple de travail, de sorte qu'ils utilisent un forte-ref pour le sous-système dont ils ont besoin. Subsystems, cependant, ne nécessitent pas un Facade de travail. Subsystems, il suffit de fournir un moyen d'avertir Facades à propos des uns et des autres actions.

class Facade:
  def __init__(self, subsystem)
    self.subsystem = subsystem
    subsystem.Register(self)

class Subsystem:
  def __init__(self):
    self.notify = []
  def Register(self, who):
    self.notify.append(weakref.proxy(who))

sub = Subsystem()
f1 = CliFacade(sub)
f2 = WebFacade(sub)
# Go on to reading from POST, stdin, etc

33voto

Alex Martelli Points 330805

Yep, weakref est excellent ici. Plus précisément, au lieu de:

self.children = {}

utilisation:

self.children = weakref.WeakValueDictionary()

Rien d'autre n'a besoin de changement dans votre code. De cette façon, quand un enfant n'a pas d'autres différences, il va juste loin-et de ce fait l'entrée dans la société mère children carte qui a l'enfant en tant que valeur.

Éviter les boucles de référence est haut sur un pied d'égalité avec la mise en œuvre de caches comme une motivation pour l'utilisation de l' weakref module. Ref boucles de ne pas vous tuer, mais ils risquent de finir le colmatage de votre mémoire, de l'esp. si certaines des classes dont les instances sont impliquées dans leur définissent __del__, depuis qu'il interfère avec l' gcs'module capacité à dissoudre ces boucles.

21voto

Denis Otkidach Points 13111

Je suggère d'utiliser child.parent = weakref.proxy(self). C'est une bonne solution pour éviter les références circulaires dans le cas où la durée de vie de (références externes à l') parent couvre la durée de vie de l' child. Contraire, utilisez weakref pour child (comme Alex suggéré) lorsque la durée de vie de l' child couvre la durée de vie de l' parent. Mais n'utilisez jamais d' weakref lorsque les deux parent et child peut être vivant sans l'autre.

Voici ces règles sont illustrées par des exemples. Utilisation weakref-ed parent si vous stockez de la racine de certaines variables et de les passer, tandis que les enfants sont accessibles à partir de ça:

def Run():
  root, c1, c2 = Node(), Node(), Node()
  root.AddChild("first", c1)
  root.AddChild("second", c2)
  return root # Note that only root refers to c1 and c2 after return, 
              # so this references should be strong

Utilisation weakref-ed enfants si vous liez tous à des variables, alors que la racine est accessible à travers eux:

def Run():
  root, c1, c2 = Node(), Node(), Node()
  root.AddChild("first", c1)
  root.AddChild("second", c2)
  return c1, c2

Mais aucun ne fonctionne pour les personnes suivantes:

def Run():
  root, c1, c2 = Node(), Node(), Node()
  root.AddChild("first", c1)
  root.AddChild("second", c2)
  return c1

1voto

max Points 6673

Je voulais préciser les références peuvent être faibles. L'approche suivante est général, mais j'utilise la double liaison de l'arbre dans tous les exemples.

Logique De L'Étape 1.

Vous devez vous assurer qu'il y a de fortes références à garder tous les objets en vie aussi longtemps que vous en avez besoin. Il peut être fait de plusieurs façons, par exemple:

  • [direct nom]: un nom de référence à chaque nœud de l'arbre
  • [container]: une référence à un conteneur qui stocke tous les nœuds
  • [root + enfants]: une référence pour le nœud racine, et des références à partir de chaque nœud à ses enfants
  • [feuilles + parent]: les références à tous les nœuds feuilles, et des références à partir de chaque nœud à son parent

Logique De L'Étape 2.

Vous pouvez maintenant ajouter des références à représenter l'information, si nécessaire.

Par exemple, si vous avez utilisé [container] approche à l'Étape 1, vous avez encore de représenter les bords. Une arête entre les nœuds A et B peuvent être représentés avec une référence unique; il peut aller dans les deux sens. Encore une fois, il existe de nombreuses options, par exemple:

  • [les enfants]: références de chaque nœud à ses enfants
  • [parent]: une référence à partir de chaque nœud à son parent
  • [set de jeux]: un ensemble contenant du 2-ensembles d'éléments; chaque 2-élément contient des références à des nœuds d'un bord

Bien sûr, si vous avez utilisé [root + enfants] approche à l'Étape 1, toutes vos informations sont déjà bien représentées, de sorte que vous ignorez cette étape.

Logique De L'Étape 3.

Vous pouvez maintenant ajouter des références à améliorer les performances, si cela est souhaité.

Par exemple, si vous avez utilisé [container] approche à l'Étape 1, et [les enfants] approche à l'Étape 2, vous pourriez désirer pour améliorer la vitesse de certains de ces algorithmes, et ajouter des références entre chaque nœud et de ses parents. De telles informations est logiquement redondant, car vous pourriez, à un coût en performance) calculer à partir de données existantes.


Toutes les références à l'Étape 1 doit être forte.

Toutes les références dans les Étapes 2 et 3 peuvent être fort ou faible. Il n'y a aucun avantage à utiliser des références fortes. Il y a un avantage à utiliser des références faibles jusqu'à ce que vous savez que les cycles ne sont plus possibles. À strictement parler, une fois que vous savez que les cycles sont impossible, il ne fait aucune différence si l'utilisation de faibles ou de fortes références. Mais pour éviter de penser à ce sujet, vous pourriez aussi bien utiliser exclusivement les références faibles dans les Étapes 2 et 3.

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