2012-10-19 13 views
5

Dizilerin karşılaştırılması için set().issubset() kullanmaya çalışıyorum. Tahmin edebileceğiniz gibi, beklendiği gibi çalışmıyor;) Önceden: uzun kod bloğu için üzgünüm.Python set() .subub() beklendiği gibi çalışmıyor

$ python 
Python 2.7.2 (v2.7.2:8527427914a2, Jun 11 2011, 15:22:34) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import issubsettest 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "issubsettest.py", line 52, in <module> 
    assert yy.issubset(xx), "issubset not working :(" 
AssertionError: issubset not working :(
>>> 

Burada ne eksik:

class T(object): 
    def __init__(self, value, attributes = None): 
    self.value = value 
    self.attributes = Attributes(attributes) 

    def __eq__(self, other): 
    if not isinstance(other, T): 
     return False 
    if self.value == other.value and self.attributes == other.attributes: 
     return True 
    else: 
     return False 

    def __ne__(self, other): 
    if not isinstance(other, T): 
     return True 
    if self.value != other.value or self.attributes != other.attributes: 
     return True 
    else: 
     return False 

class Attributes(dict): 
    def __init__(self, attributes): 
    super(dict, self) 
    self.update(attributes or dict()) 

    def __eq__(self, other): 
    if self.items() == other.items(): 
     return True 
    else: 
     return False 

    def __ne__(self, other): 
    if not self.items() == other.items(): 
     return True 
    else: 
     return False 

    def __cmp__(self, other): 
    return self.items().__cmp__(other.items()) 


x = [T("I", {'pos': 0}), T("am", {'pos': 1}), T("a", {'pos': 2}), T("test", {'pos': 3})] 
y = [T("a", {'pos': 2}), T("test", {'pos': 3})] 
xx = set(x) 
yy = set(y) 

assert y[0] == x[2], "__eq__ did fail, really?" #works 
assert y[1] == x[3], "__eq__ did fail, really?" #works 
assert xx-(yy-xx) == xx, "set subtract not working" #works, but is nonsense. see accepted answer 
assert not xx.issubset(yy), "i am doing it wrong..." #works 
assert yy.issubset(xx), "issubset not working :(" #FAILS! 

kodunun üzerinde Koşu son iddiayı başarısız? Nesneleriniz id (__hash__'u geçersiz kılmamışsınız) tarafından karma hale getirilmektedir.

+0

Bunun nedeni, kullanıcının –

+0

kullanıcısı olduğunu düşünüyorum çünkü sorun, 'T' sınıfınızın '__hash__' yöntemini geçersiz kılmamasıdır. [Belgeler] 'e bakın (http://docs.python.org/reference/datamodel.html#object.__hash__): * __cmp __() veya __eq __() işlevlerini tanımlar, ancak __hash __() öğelerini kullanmazsa, örnekleri kullanılamaz. karma koleksiyonlarda.* – halex

+2

Sadece kodunuzu Python 3.3 ile test ettim, bu da hata veriyor: 'TypeError: unhashable type: 'T' satırında 'xx = set (x)' yani kesinlikle __hash__' eksik. – halex

cevap

9
Tabii ki, xx ve yy'dan beri alt kümeler değiller, benzersiz nesneler içerirler.

Bunu yapmak için, bir çeşit __hash__ işlevi bulmanız gerekir. __hash__ her zaman bir nesne için aynı değeri döndürmelidir; bu nedenle, genellikle yıkanabilir bir nesneyi değiştirmediğiniz anlaşılmaktadır. Örneğin, bir seçim olabilir:

class T(object): 
    #<snip> ... 

    def __hash__(self): 
     return hash(self.value) 

    #... </snip> 

self.value nesnenin yaşam için değiştiremezsiniz kaldığı anlaşılır. (Not, bunun iyi bir seçimdir iddia etmiyorum kullandığınız asıl karma gerçek uygulamada gerçekten bağlıdır.) Şimdi


neden - setleri (ve dicts) arkasında büyü ve inanılmaz performans, onlar hashs dayanıyor. Temel olarak, her yıkanabilir nesne "mükemmel" (mükemmel bir dünyada) sayısına dönüşür. Python, bu "benzersiz" sayıyı alır ve nesne üzerinde bir tanıtıcı elde etmek için kullanabileceği bir dizi dizinine dönüştürür (burada sihrin açıklanması biraz zor, fakat bu tartışma için önemli değildir). Yani, "dizi" (genellikle bir masa olarak adlandırılır) içindeki diğer tüm nesnelerle karşılaştırarak bir nesneyi aramaktan ziyade, pahalı bir işlem olarak, nesne değerine dayalı olarak tam olarak nerede arayacağını bilir (ucuz) . Varsayılan olarak, kullanıcı tanımlı nesneler, id (bellek adresi) tarafından kesilmiştir. xx kümesini oluşturduğunuzda, python id s nesnesinin her birine bakar ve bunları kimliklerini temel alarak atar. Şimdi xx.issubset(yy) yaptıklarında, python tüm kimliklere xx'da bakar ve hepsinin yy içinde olup olmadığını kontrol eder. Ancak, bunlardan hiçbiri, hepsi benzersiz nesneler olduklarından (ve dolayısıyla benzersiz karma değerlere sahip olduklarından) yy içeriğindedir.

Ama diyorsunuz, "neden xx-(yy-xx) == xx çalışıyor?" İyi soru. Bunu ayırırız.

İlk olarak, (yy - xx) var. Bu boş kümeyi döndürür, çünkü xx öğesindeki hiçbir öğe yy öğesinde de yoktur (hepsi benzersiz id s olduğundan farklı değerlere sahiptirler). Yani gerçekten o True neden oldukça açık olmalı

xx - set([]) == xx 

yapıyoruz.
+0

daha sonra nasıl düzeltmesi gerektiğini açıklar. – JBernardo

+0

@JBernardo - bunu düzeltmek hep birlikte başka bir hikaye. OP'nin bir şekilde __hash__'ı geçersiz kılması gerekiyor. Ama uygun bir '__hash__' seçmek gerçekten bunun için ne kullanacağına bağlı. – mgilson

+0

Evet, ona bunu söyle. – JBernardo