2013-02-03 8 views
0

Je suis tombé sur un problème aujourd'hui bizarre, voici quelques exemples de codeComportement inattendu avec defaultdict

from collections import defaultdict 

class Counter: 
    hits = 0 
    visitors = set() 

    def addHit(self, ip): 
     self.hits += 1 
     self.visitors.add(ip) 

d = defaultdict(Counter) 
d['a'].addHit('1.1.1') 
d['a'].addHit('2.2.2') 
d['b'].addHit('3.3.3') 

print d['a'].hits, d['a'].visitors 
print d['b'].hits, d['b'].visitors 

Résultat attendu:

2 set(['1.1.1', '2.2.2']) 
1 set(['3.3.3']) 

Résultat réel:

2 set(['1.1.1', '3.3.3', '2.2.2']) 
1 set(['1.1.1', '3.3.3', '2.2.2']) 

Pourquoi la Le visiteur définit le partage des données entre ce que je pensais être des instances séparées de la classe Counter. Chaque entrée ne devrait-elle pas pointer vers une instance spécifique? Ce qui rend cela plus difficile à comprendre, c'est que le compteur semble fonctionner correctement et garder les choses séparées.

Quelqu'un peut-il m'aider à comprendre ce qui se passe ici ou comment y remédier?

+0

formulé différemment, mais même problème que http://stackoverflow.com/questions/14667465/multiple-instances-of-a-python-object-are-acting-like-the-same- instance – tacaswell

Répondre

4

Je suppose que vos visiteurs sont définis comme une variable de classe et non comme une variable d'instance.

Rien à voir avec le comportement defaultdicts.

Essayez:

class Counter: 
    def __init__(self): 
     self.hits = 0 
     self.visitors = set() 

    def addHit(self, ip): 
     self.hits += 1 
     self.visitors.add(ip) 

EDIT: Rien à voir avec vos questions, mais seulement quelques idées sur la façon d'élargir votre comptoir:

#! /usr/bin/python3.2 

class Counter: 
    def __init__(self): 
     self.__hits = 0 
     self.__visitors = {} 

    def addHit(self, ip): 
     self.__hits += 1 
     if ip not in self.__visitors: 
      self.__visitors [ip] = 0 
     self.__visitors [ip] += 1 

    @property 
    def hits (self): 
     return self.__hits 

    @property 
    def uniqueHits (self): 
     return len (self.__visitors) 

    @property 
    def ips (self): 
     return (ip for ip in self.__visitors) 

    def __getitem__ (self, ip): 
     return 0 if ip not in self.__visitors else self.__visitors [ip] 

c = Counter() 

c.addHit ('1.1.1.1') 
c.addHit ('1.1.1.1') 
c.addHit ('1.1.1.1') 
c.addHit ('1.1.1.1') 
c.addHit ('1.1.1.2') 
c.addHit ('1.1.1.2') 
c.addHit ('1.1.1.3') 

print (c.hits) 
print (c.uniqueHits) 
for ip in c.ips: 
    print (ip, c [ip]) 
+0

Merci pour cela, la solution fonctionne. Je ne suis pas sûr que j'ai besoin de compter le nombre de visites de chaque visiteur unique, mais merci d'étendre un peu –

2

Ceci est exactement le même problème que Multiple Instances of a Python Object are acting like the same instance

Vous utilisez une variable de niveau classe. Changer à

class Counter: 
    def __init__(self): 
     self.hits = 0 
     self.visitors = set() 

    def addHit(self, ip): 
     self.hits += 1 
     self.visitors.add(ip) 
+1

Haha. Deux stupides, une pensée. Toi et moi avons posté exactement le même code. – Hyperboreus

+0

@Hyperboreus Eh bien, il y a le morceau du manifeste qui dit qu'il devrait y avoir une seule façon de faire les choses en python;) – tacaswell

+0

Et pour ceux qui se demandent comment ils peuvent lire le "manifeste pythoniste", simplement "importer ceci" ... alors, pour un peu plus de plaisir, lisez la source :) – mgilson