2013-03-22 8 views
5

Ben nesnelerin belirledik:Nesneler için kümede çiftler nasıl kaldırılır?

class Test(object): 
    def __init__(self): 
     self.i = random.randint(1,10) 


res = set() 

for i in range(0,1000): 
    res.add(Test()) 

print len(res) = 1000 

Nasıl nesnelerin kümesinden çiftleri kaldırmak için?

class Test(object): 
    def __init__(self, i): 
     self.i = i 
    # self.i = random.randint(1,10) 
    # self.j = random.randint(1,20) 

    def __keys(self): 
     t =() 
     for key in self.__dict__: 
      t = t + (self.__dict__[key],) 
     return t 

    def __eq__(self, other): 
     return isinstance(other, Test) and self.__keys() == other.__keys() 

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

res = set() 

res.add(Test(2)) 
... 
res.add(Test(8)) 

sonuç: [2,8,3,4,5,6,7]

ama nasıl kurtarmak için cevaplar

sayesinde iştir? Destek siparişini ayarlar. Örneğin bunun yerine liste kullanabilir miyim?

cevap

9

Kişisel nesneleri olmalıdır hashable (yani __eq__() ve __hash__() tanımlanmış olması gerekir) setleri onlarla düzgün çalışması için: o hiç değişmez bir karma değeri varsa

class Test(object): 
    def __init__(self): 
     self.i = random.randint(1, 10) 

    def __eq__(self, other): 
     return self.i == other.i 

    def __hash__(self): 
     return self.i 

bir nesne hashable olduğunu ömrü boyunca (bir __hash__() yöntemine ihtiyaç duyar) ve diğer nesnelere göre karşılaştırılabilir (bir __eq__() veya __cmp__() yöntemine ihtiyaç duyar). Eşitliği karşılaştıran yıkanabilir nesneler aynı karma değere sahip olmalıdır.

Hashability, bir sözlük anahtarı ve bir set üyesi olarak kullanılabilen bir nesneyi oluşturur, çünkü bu veri yapıları karma değerini dahili olarak kullanır.

 

birkaç özelliklerini, karma var ve bunların bir demet karşılaştırırsanız (teşekkürler, delnan):

class Test(object): 
    def __init__(self): 
     self.i = random.randint(1, 10) 
     self.k = random.randint(1, 10) 
     self.j = random.randint(1, 10) 

    def __eq__(self, other): 
     return (self.i, self.k, self.j) == (other.i, other.k, other.j) 

    def __hash__(self): 
     return hash((self.i, self.k, self.j)) 
+0

Teşekkürler, ama birkaç tane var mı? – Bdfy

+0

Eşsiz bir tamsayı karması üretmek için bunları karıştırın (buitlin 'karma 'işlevini tamsayı olmayan özniteliklerde kullanın ve'^birlikte bunları birlikte kullanın) ve eşitliği sizin için anlamlı bir şekilde tanımlayın. Bu iki nesnenin, kopyalarını değerlendirmek için ortak olması gereken şey nedir? Bunu __eq__'de ifade edin. –

+2

Eşitliği ve hashı tanımlamanın en kolay yolu, nesnenize izomorfik olan bir tuple bulmak ve sonra tuple '__hash__' ve' __eq__' için 'collections.namedtuple' (eğer varsa) kullanarak veya tuples oluşturarak talep üzerine: 'def __hash __ (self): dönüş hash ((self.x, self.y, self.z))'. – delnan

0

Ben kolayca bir liste olarak sahip istediğini yapabileceğini düşünüyorum eq operatörünü tanımladığınız ilk postanızda sordunuz:

l = [] 
if Test(0) not in l : 
    l.append(Test(0)) 

Benim 2 cts ...

0

Pavel Anossov'un cevabı, sınıfınızın istediğiniz semantiklerle birlikte kullanılmasına izin vermek için mükemmeldir. Bununla birlikte, öğelerinizin sırasını korumak istiyorsanız biraz daha fazlasına ihtiyacınız olacak. İşte bir liste de-çoğaltan bir fonksiyon olarak uzun bir liste öğeleri hashable olarak verilmiştir:

def dedupe(lst): 
    seen = set() 
    results = [] 
    for item in lst: 
     if item not in seen: 
      seen.add(item) 
      results.append(item) 
    return results 

Biraz daha deyimsel versiyonu yerine bir liste döndüren bir işlev yerine bir jeneratör olacaktır. Bu, benzersiz değerleri ona eklemek yerine yield kullanarak results değişkeninden kurtulur. Aynı zamanda, herhangi bir yinelenen nesnede (başka bir jeneratör gibi) çalışacağı içinparametresini iterable olarak yeniden adlandırdım.

def dedupe(iterable): 
    seen = set() 
    for item in iterable: 
     if item not in seen: 
      seen.add(item) 
      yield item 
+0

Bunu kendiniz yazmak zorunda değilsiniz; Zaten [itertools tarifleri] (http://docs.python.org/2/library/itertools.html#recipes) 'unique_everseen' olarak. Zaten yazılı ve iyi test edilmiş ve optimize edilmiş olmasının yanı sıra, bir 'anahtar 'işlevi de alır. Bu yüzden, sadece kodunuza kopyalayın ve onu kullanın ya da daha fazla itertools'u kurun ve oradan içe aktarın. – abarnert

1

İlk sorunuz zaten Pavel Anossov tarafından cevaplandırılmıştır.

Ama başka bir soru var:

ama nasıl kurtarmak için? Destek siparişini ayarlar.Örneğin bunun yerine liste kullanabilir miyim?

Sen bir list kullanabilirsiniz, ancak birkaç olumsuz yanları vardır:

  • Yanlış arayüzü olsun.
  • Yinelenen kopyaları otomatik olarak almazsınız. Açıkça if foo not in res: res.append(foo) yazmalısınız. Açıkçası, bunu tekrar tekrar yazmak yerine bir işlev içinde tamamlayabilirsiniz, ama yine de ekstra bir iş.
  • Koleksiyonun büyük olması durumunda çok daha az verimli olacaktır. Temel olarak, yeni bir eleman eklemek, bir elemanın var olup olmadığını kontrol etmek, vs. O (N) yerine O (1) olacaktır.

İstediğinizi, sıralı bir set gibi çalışan bir şeydir. Ya da eşdeğer olarak, kopyalara izin vermeyen bir list gibi.

Bunu yaparsanız tüm senin ilk ekler ve sonra tüm aramaları ve size öncelikle bir list bina çiftleri kaldırmak itertools recipes dan unique_everseen kullanarak bu sorunun üstesinden gelebilirsiniz, aramaları hızlı olmak gerekmez.

Ya da sadece bir set ve list veya öğeleri emriyle (veya list artı şimdiye kadar görülen unsurların bir set) tutabilir. Ama bu biraz karmaşık olabilir, bu yüzden onu sarmak isteyebilirsiniz.

İdeal olarak, set ile tam olarak aynı API'ye sahip bir türe sarmak istersiniz. OrderedSet gibi bir şey collections.OrderedDict'a benziyor.

Neyse ki, bu dokümanlar sayfasının en altına giderseniz, tam olarak istediğinizi göreceksiniz. ActiveState'de OrderedSet tarifine bir bağlantı var.

Yani kopyalayın, kodunuza yapıştırın, ardından res = set()'u res = OrderedSet() olarak değiştirin ve işiniz bitti.

İlgili konular