Python: Anahtarlar / sözlükler, seçme, sıralama


104

Diyelim ki farklı renklerde meyvelerim var, örneğin, 24 mavi muz, 12 yeşil elma, 0 mavi çilek vb. Bunları Python'da kolay seçime ve sıralamaya izin veren bir veri yapısında düzenlemek istiyorum. Benim fikrim onları anahtar olarak başlıklar içeren bir sözlüğe koymaktı, ör.

{ ('banana',    'blue' ): 24,
  ('apple',     'green'): 12,
  ('strawberry','blue' ): 0,
  ...
}

hatta sözlükler, ör.

{ {'fruit': 'banana',    'color': 'blue' }: 24,
  {'fruit': 'apple',     'color': 'green'}: 12,
  {'fruit': 'strawberry','color': 'blue' }: 0,
  ...
}

Örneğin, tüm mavi meyvelerin veya her renkteki muzların bir listesini almak veya bu sözlüğü meyvenin adına göre sıralamak istiyorum. Bunu temiz bir şekilde yapmanın yolları var mı?

Anahtar olarak başlıklar içeren sözlükler bu durumu halletmek için uygun bir yol olmayabilir .

Tüm öneriler hoş geldiniz!


26
Bir veritabanı istiyormuşsun gibi geliyor ...
Adam Rosenfield

4
Bu değerlerin farklı koleksiyonlarını koordine etmeye çalışmak yerine, bu verileri modellemek için bir clsas tanımlamak en iyisi olur
Cuga

2
@AdamRosenfield belki bir tane inşa ediyor.
Prof. Falken

Sadece bir sözlüğün hashable olmadığını eklemek istedim, bu yüzden sormak mümkün olmadığından ikinci sözdizimi mümkün değil çünkü bir sözlük olan {'meyve': 'muz', 'renk': 'mavi'} anahtar olarak kullanılamaz başka bir sözlük için. bir TypeError: unhashable type: 'dict'e neden olur.
epeleg

Yanıtlar:


147

Şahsen, python hakkında sevdiğim şeylerden biri, tuple-dikt kombinasyonu. Burada sahip olduğunuz şey, etkili bir şekilde bir 2d dizisi (burada x = meyve adı ve y = renk) ve ben genellikle, en azından benzeri numpybir şey veya bir veritabanı daha uygun olmadığında , 2d dizilerini uygulamak için tuple diktesinin destekçisiyim . Kısacası, iyi bir yaklaşımın olduğunu düşünüyorum.

Ekstra çalışma yapmadan diktleri bir diktede anahtar olarak kullanamayacağınızı unutmayın, bu yüzden bu pek iyi bir çözüm değildir.

Bununla birlikte, namedtuple () 'yi de düşünmelisiniz . Bu şekilde bunu yapabilirsiniz:

>>> from collections import namedtuple
>>> Fruit = namedtuple("Fruit", ["name", "color"])
>>> f = Fruit(name="banana", color="red")
>>> print f
Fruit(name='banana', color='red')
>>> f.name
'banana'
>>> f.color
'red'

Şimdi meyve sayımı diktenizi kullanabilirsiniz:

>>> fruitcount = {Fruit("banana", "red"):5}
>>> fruitcount[f]
5

Diğer numaralar:

>>> fruits = fruitcount.keys()
>>> fruits.sort()
>>> print fruits
[Fruit(name='apple', color='green'), 
 Fruit(name='apple', color='red'), 
 Fruit(name='banana', color='blue'), 
 Fruit(name='strawberry', color='blue')]
>>> fruits.sort(key=lambda x:x.color)
>>> print fruits
[Fruit(name='banana', color='blue'), 
 Fruit(name='strawberry', color='blue'), 
 Fruit(name='apple', color='green'), 
 Fruit(name='apple', color='red')]

Chmullig'i yankılamak, bir meyvenin tüm renklerinin bir listesini almak için anahtarları filtrelemeniz gerekir, yani

bananas = [fruit for fruit in fruits if fruit.name=='banana']

#senderle Başka bir yanıta yorum olarak yazdınız "Ama içimden gelen his, bir veritabanının OP'nin ihtiyaçları için gereğinden fazla olduğu;"; Bu nedenle, adlandırılmış bir ikili alt sınıf oluşturmayı tercih edersiniz. Ancak, verilerini işlemek için kendi araçlarına sahip mikro veritabanları değilse, sınıfların örnekleri nelerdir?
eyquem

Şu alt listelerden çıkarabilir miyim name='banana'?
Nico Schlömer

2
Chmullig'in belirttiği gibi, anahtarları filtrelemeniz gerekir, yani bananas = filter(lambda fruit: fruit.name=='banana', fruits)veya bananas = [fruit for fruit in fruits if fruit.name=='banana']. Bu, iç içe yerleştirilmiş diktelerin potansiyel olarak daha verimli olmasının bir yoludur; her şey, verileri kullanmayı planladığınız yöntemlerle ilgilidir.
gönderen

adlandırılmış diziye daha fazla anahtar eklememek işleri kolaylaştırmaz mı? Yeni bir özellik eklemeyi söyleyebilirimcount
openrijal

18

En iyi seçeneğiniz, sahip olduklarınızı modellemek için basit bir veri yapısı oluşturmak olacaktır. Daha sonra bu nesneleri basit bir listede saklayabilir ve istediğiniz şekilde sıralayabilir / geri alabilirsiniz.

Bu durum için aşağıdaki sınıfı kullanırım:

class Fruit:
    def __init__(self, name, color, quantity): 
        self.name = name
        self.color = color
        self.quantity = quantity

    def __str__(self):
        return "Name: %s, Color: %s, Quantity: %s" % \
     (self.name, self.color, self.quantity)

Ardından, aşağıdaki şekilde gösterildiği gibi "Fruit" örneklerini oluşturabilir ve bunları bir listeye ekleyebilirsiniz:

fruit1 = Fruit("apple", "red", 12)
fruit2 = Fruit("pear", "green", 22)
fruit3 = Fruit("banana", "yellow", 32)
fruits = [fruit3, fruit2, fruit1] 

Basit liste fruitsçok daha kolay, daha az kafa karıştırıcı ve daha iyi korunacaktır.

Bazı kullanım örnekleri:

Aşağıdaki tüm çıktılar, verilen kod parçacığını çalıştırdıktan sonra elde edilen sonuçtur ve ardından:

for fruit in fruits:
    print fruit

Sıralanmamış liste:

Görüntüler:

Name: banana, Color: yellow, Quantity: 32
Name: pear, Color: green, Quantity: 22
Name: apple, Color: red, Quantity: 12

Ada göre alfabetik olarak sıralandı:

fruits.sort(key=lambda x: x.name.lower())

Görüntüler:

Name: apple, Color: red, Quantity: 12
Name: banana, Color: yellow, Quantity: 32
Name: pear, Color: green, Quantity: 22

Miktara göre sıralı:

fruits.sort(key=lambda x: x.quantity)

Görüntüler:

Name: apple, Color: red, Quantity: 12
Name: pear, Color: green, Quantity: 22
Name: banana, Color: yellow, Quantity: 32

Renk == kırmızı nerede:

red_fruit = filter(lambda f: f.color == "red", fruits)

Görüntüler:

Name: apple, Color: red, Quantity: 12

17

Veri tabanı, dikteler, sözlükler listesi, tuple (bir alt sınıf), sqlite, fazlalık ... Gözlerime inanmadım. Başka ?

"Anahtar olarak başlıklar içeren sözlükler bu durumu halletmek için uygun bir yol olmayabilir."

"İçimden gelen his, bir veritabanının OP'nin ihtiyaçları için gereğinden fazla olduğu;"

Evet! düşündüm

Yani, bence, bir tuple listesi yeterli:

from operator import itemgetter

li = [  ('banana',     'blue'   , 24) ,
        ('apple',      'green'  , 12) ,
        ('strawberry', 'blue'   , 16 ) ,
        ('banana',     'yellow' , 13) ,
        ('apple',      'gold'   , 3 ) ,
        ('pear',       'yellow' , 10) ,
        ('strawberry', 'orange' , 27) ,
        ('apple',      'blue'   , 21) ,
        ('apple',      'silver' , 0 ) ,
        ('strawberry', 'green'  , 4 ) ,
        ('banana',     'brown'  , 14) ,
        ('strawberry', 'yellow' , 31) ,
        ('apple',      'pink'   , 9 ) ,
        ('strawberry', 'gold'   , 0 ) ,
        ('pear',       'gold'   , 66) ,
        ('apple',      'yellow' , 9 ) ,
        ('pear',       'brown'  , 5 ) ,
        ('strawberry', 'pink'   , 8 ) ,
        ('apple',      'purple' , 7 ) ,
        ('pear',       'blue'   , 51) ,
        ('chesnut',    'yellow',  0 )   ]


print set( u[1] for u in li ),': all potential colors'
print set( c for f,c,n in li if n!=0),': all effective colors'
print [ c for f,c,n in li if f=='banana' ],': all potential colors of bananas'
print [ c for f,c,n in li if f=='banana' and n!=0],': all effective colors of bananas'
print

print set( u[0] for u in li ),': all potential fruits'
print set( f for f,c,n in li if n!=0),': all effective fruits'
print [ f for f,c,n in li if c=='yellow' ],': all potential fruits being yellow'
print [ f for f,c,n in li if c=='yellow' and n!=0],': all effective fruits being yellow'
print

print len(set( u[1] for u in li )),': number of all potential colors'
print len(set(c for f,c,n in li if n!=0)),': number of all effective colors'
print len( [c for f,c,n in li if f=='strawberry']),': number of potential colors of strawberry'
print len( [c for f,c,n in li if f=='strawberry' and n!=0]),': number of effective colors of strawberry'
print

# sorting li by name of fruit
print sorted(li),'  sorted li by name of fruit'
print

# sorting li by number 
print sorted(li, key = itemgetter(2)),'  sorted li by number'
print

# sorting li first by name of color and secondly by name of fruit
print sorted(li, key = itemgetter(1,0)),'  sorted li first by name of color and secondly by name of fruit'
print

sonuç

set(['blue', 'brown', 'gold', 'purple', 'yellow', 'pink', 'green', 'orange', 'silver']) : all potential colors
set(['blue', 'brown', 'gold', 'purple', 'yellow', 'pink', 'green', 'orange']) : all effective colors
['blue', 'yellow', 'brown'] : all potential colors of bananas
['blue', 'yellow', 'brown'] : all effective colors of bananas

set(['strawberry', 'chesnut', 'pear', 'banana', 'apple']) : all potential fruits
set(['strawberry', 'pear', 'banana', 'apple']) : all effective fruits
['banana', 'pear', 'strawberry', 'apple', 'chesnut'] : all potential fruits being yellow
['banana', 'pear', 'strawberry', 'apple'] : all effective fruits being yellow

9 : number of all potential colors
8 : number of all effective colors
6 : number of potential colors of strawberry
5 : number of effective colors of strawberry

[('apple', 'blue', 21), ('apple', 'gold', 3), ('apple', 'green', 12), ('apple', 'pink', 9), ('apple', 'purple', 7), ('apple', 'silver', 0), ('apple', 'yellow', 9), ('banana', 'blue', 24), ('banana', 'brown', 14), ('banana', 'yellow', 13), ('chesnut', 'yellow', 0), ('pear', 'blue', 51), ('pear', 'brown', 5), ('pear', 'gold', 66), ('pear', 'yellow', 10), ('strawberry', 'blue', 16), ('strawberry', 'gold', 0), ('strawberry', 'green', 4), ('strawberry', 'orange', 27), ('strawberry', 'pink', 8), ('strawberry', 'yellow', 31)]   sorted li by name of fruit

[('apple', 'silver', 0), ('strawberry', 'gold', 0), ('chesnut', 'yellow', 0), ('apple', 'gold', 3), ('strawberry', 'green', 4), ('pear', 'brown', 5), ('apple', 'purple', 7), ('strawberry', 'pink', 8), ('apple', 'pink', 9), ('apple', 'yellow', 9), ('pear', 'yellow', 10), ('apple', 'green', 12), ('banana', 'yellow', 13), ('banana', 'brown', 14), ('strawberry', 'blue', 16), ('apple', 'blue', 21), ('banana', 'blue', 24), ('strawberry', 'orange', 27), ('strawberry', 'yellow', 31), ('pear', 'blue', 51), ('pear', 'gold', 66)]   sorted li by number

[('apple', 'blue', 21), ('banana', 'blue', 24), ('pear', 'blue', 51), ('strawberry', 'blue', 16), ('banana', 'brown', 14), ('pear', 'brown', 5), ('apple', 'gold', 3), ('pear', 'gold', 66), ('strawberry', 'gold', 0), ('apple', 'green', 12), ('strawberry', 'green', 4), ('strawberry', 'orange', 27), ('apple', 'pink', 9), ('strawberry', 'pink', 8), ('apple', 'purple', 7), ('apple', 'silver', 0), ('apple', 'yellow', 9), ('banana', 'yellow', 13), ('chesnut', 'yellow', 0), ('pear', 'yellow', 10), ('strawberry', 'yellow', 31)]   sorted li first by name of color and secondly by name of fruit

1
Merhaba, çözümünüzü beğendim ancak işlem karmaşıklığı konularını ele almıyor. tüm arama türleri liste boyutunda astardır (O (n)). OP'nin bazı eylemlerin diğerlerinden daha hızlı olmasını istemesi mantıklı olacaktır (örneğin, sarı muz sayısını elde etmek, O (1) 'de mümkün olmasını beklediğim bir şey olurdu.
epeleg

13

Bir sözlük muhtemelen bu durumda kullanmanız gereken şey değildir. Daha tam özellikli bir kitaplık daha iyi bir alternatif olabilir. Muhtemelen gerçek bir veritabanı. En kolayı sqlite olacaktır . Bir dosya adı yerine ': memory:' dizesini ileterek her şeyi bellekte tutabilirsiniz.

Bu yola devam etmek istiyorsanız, bunu anahtardaki veya değerdeki ekstra özniteliklerle yapabilirsiniz. Bununla birlikte, bir sözlük başka bir sözlüğün anahtarı olamaz, ancak bir demet olabilir. Dokümanlar neyin izin verilebileceğini açıklıyor. Yalnızca dizeleri ve sayıları içeren dizeleri, sayıları ve demetleri (ve yalnızca bu türleri yinelemeli olarak içeren daha fazla tuple ...) içeren değişmez bir nesne olmalıdır.

İlk örneğinizi yapabilirsiniz d = {('apple', 'red') : 4}, ancak ne istediğinizi sorgulamak çok zor olacaktır. Bunun gibi bir şey yapmanız gerekir:

#find all apples
apples = [d[key] for key in d.keys() if key[0] == 'apple']

#find all red items
red = [d[key] for key in d.keys() if key[1] == 'red']

#the red apple
redapples = d[('apple', 'red')]

4
Bu yanıta olumsuz oy vermedim ve vermedim çünkü daha büyük ölçeklerde veritabanları (tabii ki!) En iyi yol. Ama içimden gelen his, bir veritabanının OP'nin ihtiyaçları için gereğinden fazla olduğu; belki bu olumsuz oyu açıklar?
senderle

4

Anahtarlar tuple olarak kullanıldığında, anahtarları verilen ikinci bileşenle filtreleyin ve sıralayın:

blue_fruit = sorted([k for k in data.keys() if k[1] == 'blue'])
for k in blue_fruit:
  print k[0], data[k] # prints 'banana 24', etc

Sıralama, bileşenlerinin doğal sıralaması varsa, demetler doğal bir sıraya sahip olduğu için işe yarar.

Tam teşekküllü nesneler gibi anahtarlarla, sadece filtrelersiniz k.color == 'blue'.

Dicts'i gerçekten anahtar olarak kullanamazsınız, ancak buna benzer en basit bir sınıf oluşturabilir class Foo(object): passve anında ona herhangi bir nitelik ekleyebilirsiniz:

k = Foo()
k.color = 'blue'

Bu örnekler, dikte anahtarları olarak hizmet edebilir, ancak değişkenliklerine dikkat edin!


3

Girişlerin diğer sözlüklerin bir listesi olduğu bir sözlüğe sahip olabilirsiniz:

fruit_dict = dict()
fruit_dict['banana'] = [{'yellow': 24}]
fruit_dict['apple'] = [{'red': 12}, {'green': 14}]
print fruit_dict

Çıktı:

{'muz': [{'sarı': 24}], 'elma': [{'kırmızı': 12}, {'yeşil': 14}]}

Düzenleme: eumiro'nun da belirttiği gibi, bir sözlükler sözlüğü kullanabilirsiniz:

fruit_dict = dict()
fruit_dict['banana'] = {'yellow': 24}
fruit_dict['apple'] = {'red': 12, 'green': 14}
print fruit_dict

Çıktı:

{"muz": {"sarı": 24}, "elma": {"yeşil": 14, "kırmızı": 12}}


2
Sözlük listesi sözlüğü? Belki sözlük sözlüğü yeterli olur?
eumiro

@eumiro: Teşekkürler, haklısın ve bu benim orijinal fikrimdi. Ancak, orijinal örneği kodlarken bunu bir dikt listesi diktesine dönüştürdüm. Bir dikt örneği ekledim.
GreenMatt

İç içe geçmiş sözlükler kafa karıştırıcı olma eğilimindedir. Lütfen
cevabıma

@Cuga: Diktatörlüklerin vs. kafa karıştırıcı olabileceğine katılıyorum. @ Nico'nun sorduğu soruyu yanıtlamak için açıklayıcı bir örnek veriyorum.
GreenMatt

Özür dilerim: Çözümünüzün yanlış olduğunu ima etmek istemedim; açıkça işe yarıyor ve bazı durumlarda ideal olabilir. Durumla ilgili görüşümü paylaşmak istedim.
Cuga

2

Bu tür veriler, Trie benzeri bir veri yapısından verimli bir şekilde çekilir. Aynı zamanda hızlı sıralama sağlar. Bellek verimliliği o kadar büyük olmayabilir.

Geleneksel bir üçlü, bir kelimenin her harfini ağaçta bir düğüm olarak depolar. Ama senin durumunda "alfaben" farklı. Karakterler yerine dizeler depoluyorsunuz.

şunun gibi görünebilir:

root:                Root
                     /|\
                    / | \
                   /  |  \     
fruit:       Banana Apple Strawberry
              / |      |     \
             /  |      |      \
color:     Blue Yellow Green  Blue
            /   |       |       \
           /    |       |        \
end:      24   100      12        0

bu bağlantıya bakın: python'da trie


2

İki anahtarı bağımsız olarak kullanmak istiyorsunuz, bu nedenle iki seçeneğiniz var:

  1. Verileri yedekli olarak {'banana' : {'blue' : 4, ...}, .... }ve olarak iki dikte depolayın {'blue': {'banana':4, ...} ...}. Ardından, arama ve sıralama kolaydır, ancak diktleri birlikte değiştirdiğinizden emin olmalısınız.

  2. Yalnızca bir dikte kaydedin ve ardından bunların üzerinde yinelenen işlevler yazın, örneğin:

    d = {'banana' : {'blue' : 4, 'yellow':6}, 'apple':{'red':1} }
    
    blueFruit = [(fruit,d[fruit]['blue']) if d[fruit].has_key('blue') for fruit in d.keys()]

Cevabımdaki kodun neden doğru biçimde görünmediğini anlayamıyorum. Son iki satırı kod olarak düzenlemeyi ve işaretlemeyi denedim ama işe yaramıyor!
highBandWidth

1
numaralandırılmış bir liste oluşturdunuz ve ayrıştırıcı kodu (4 boşluk girintili) bu listenin ikinci öğesinin devamı olarak yorumluyor. Toplamda 8 olmak üzere kodu 4 boşluk daha girin ve ayrıştırıcı kodu kod olarak tanıyacak ve doğru şekilde biçimlendirecektir.
senderle
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.