Python: Tuples / từ điển làm khóa, chọn, sắp xếp


104

giả sử tôi có số lượng trái cây có màu sắc khác nhau, ví dụ: 24 quả chuối xanh, 12 quả táo xanh, 0 quả dâu tây xanh, v.v. Tôi muốn sắp xếp chúng theo cấu trúc dữ liệu bằng Python cho phép dễ dàng lựa chọn và sắp xếp. Ý tưởng của tôi là đưa chúng vào một từ điển với các bộ giá trị làm chìa khóa, ví dụ:

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

hoặc thậm chí từ điển, ví dụ:

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

Ví dụ, tôi muốn lấy một danh sách tất cả các loại trái cây màu xanh, hoặc chuối có đủ màu, hoặc sắp xếp từ điển này theo tên của loại trái cây. Có những cách nào để làm điều này một cách sạch sẽ?

Có thể là từ điển với các bộ giá trị làm chìa khóa không phải là cách thích hợp để xử lý tình huống này.

Tất cả các đề xuất được chào đón!


26
Có vẻ như bạn muốn có một cơ sở dữ liệu ...
Adam Rosenfield

4
Tốt nhất bạn nên xác định clsas để lập mô hình dữ liệu này, thay vì cố gắng phối hợp các bộ sưu tập khác nhau của các giá trị này
Cuga

2
@AdamRosenfield có thể anh ấy đang xây dựng một cái.
GS Falken

Chỉ muốn nói thêm rằng từ điển không thể băm được nên không thể sử dụng cú pháp thứ hai mà bạn yêu cầu vì {'fruit': 'banana', 'color': 'blue'} không thể dùng từ điển làm khóa cho từ điển khác. nó sẽ gây ra TypeError: loại không thể chỉnh sửa: 'dict'.
epeleg

Câu trả lời:


147

Cá nhân tôi, một trong những điều tôi yêu thích về python là sự kết hợp tuple-dict. Những gì bạn có ở đây thực sự là một mảng 2d (trong đó x = tên quả và y = màu) và tôi nói chung là người ủng hộ mệnh lệnh của các bộ giá trị để triển khai mảng 2d, ít nhất là khi một cái gì đó tương tự numpyhoặc cơ sở dữ liệu không thích hợp hơn . Tóm lại, tôi nghĩ bạn đã có một cách tiếp cận tốt.

Lưu ý rằng bạn không thể sử dụng các dấu chấm làm phím trong một câu chính tả mà không thực hiện thêm một số thao tác, vì vậy đó không phải là một giải pháp tốt.

Điều đó nói rằng, bạn cũng nên xem xét têntuple () . Bằng cách đó, bạn có thể làm điều này:

>>> 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'

Bây giờ bạn có thể sử dụng tài khoản trái cây của mình:

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

Các thủ thuật khác:

>>> 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')]

Echoing chmullig, để có được danh sách tất cả các màu của một loại trái cây, bạn sẽ phải lọc các phím, tức là

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

#senderle Bạn đã viết như một bình luận cho một câu trả lời khác "Nhưng cảm giác gan ruột của tôi là cơ sở dữ liệu quá mức cần thiết cho nhu cầu của OP;"; Vì vậy, bạn thích tạo một lớp con được đặt tên. Nhưng những trường hợp khác của các lớp là gì nếu không phải là cơ sở dữ liệu vi mô với các công cụ riêng để xử lý dữ liệu của chúng?
eyquem

Tôi có thể từ những danh sách phụ trích xuất với name='banana'?
Nico Schlömer

2
Như chmullig đã chỉ ra, bạn sẽ phải lọc các phím, tức là bananas = filter(lambda fruit: fruit.name=='banana', fruits)hoặc bananas = [fruit for fruit in fruits if fruit.name=='banana']. Đây là một cách mà các phái lồng nhau có tiềm năng hiệu quả hơn; tất cả phụ thuộc vào cách bạn định sử dụng dữ liệu.
gửi

sẽ không thêm một khóa trong tuple đã đặt tên để làm cho mọi thứ dễ dàng hơn? Tôi muốn nói thêm một thuộc tính mớicount
openrijal

18

Lựa chọn tốt nhất của bạn sẽ là tạo một cấu trúc dữ liệu đơn giản để mô hình hóa những gì bạn có. Sau đó, bạn có thể lưu trữ các đối tượng này trong một danh sách đơn giản và sắp xếp / truy xuất chúng theo bất kỳ cách nào bạn muốn.

Đối với trường hợp này, tôi sẽ sử dụng lớp sau:

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)

Sau đó, bạn có thể chỉ cần tạo các phiên bản "Fruit" và thêm chúng vào danh sách, như được hiển thị theo cách sau:

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

Danh sách đơn giản fruitssẽ dễ dàng hơn, ít gây nhầm lẫn hơn và được duy trì tốt hơn.

Một số ví dụ sử dụng:

Tất cả kết quả đầu ra bên dưới là kết quả sau khi chạy đoạn mã nhất định, theo sau là:

for fruit in fruits:
    print fruit

Danh sách chưa được sắp xếp:

Hiển thị:

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

Được sắp xếp theo thứ tự bảng chữ cái theo tên:

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

Hiển thị:

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

Sắp xếp theo số lượng:

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

Hiển thị:

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

Ở đâu màu == đỏ:

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

Hiển thị:

Name: apple, Color: red, Quantity: 12

17

Cơ sở dữ liệu, dict of dicts, từ điển danh sách các từ điển, tên tuple (nó là một lớp con), sqlite, dự phòng ... Tôi không tin vào mắt mình. Còn gì nữa?

"Có thể là từ điển với các bộ giá trị làm chìa khóa không phải là cách thích hợp để xử lý tình huống này."

"cảm giác ruột của tôi là cơ sở dữ liệu quá mức cần thiết cho nhu cầu của OP;"

Vâng! tôi đã nghĩ

Vì vậy, theo ý kiến ​​của tôi, một danh sách các bộ giá trị là đủ:

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

kết quả

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
Xin chào, tôi thích giải pháp của bạn, tuy nhiên nó không giải quyết các vấn đề phức tạp trong hoạt động. tất cả các kiểu tìm kiếm đều là liner (O (n)) trong kích thước của danh sách. trong khi đó sẽ có ý nghĩa rằng OP muốn có một số hành động được nhanh hơn sau đó những người khác (ví dụ như nhận được đếm chuối vàng sẽ là một cái gì đó mà tôi mong chờ để có thể trong thời gian O (1).
epeleg

13

Từ điển có lẽ không phải là thứ bạn nên sử dụng trong trường hợp này. Một thư viện đầy đủ tính năng hơn sẽ là một lựa chọn thay thế tốt hơn. Có lẽ là một cơ sở dữ liệu thực. Đơn giản nhất sẽ là sqlite . Bạn có thể giữ toàn bộ nội dung trong bộ nhớ bằng cách chuyển vào chuỗi ': memory:' thay vì tên tệp.

Nếu bạn muốn tiếp tục theo con đường này, bạn có thể làm điều đó với các thuộc tính bổ sung trong khóa hoặc giá trị. Tuy nhiên, một từ điển không thể là chìa khóa của một từ điển khác, nhưng một bộ tuple thì có thể. Tài liệu giải thích những gì được phép. Nó phải là một đối tượng không thay đổi, bao gồm chuỗi, số và bộ giá trị chỉ chứa chuỗi và số (và nhiều bộ dữ liệu khác chỉ chứa những loại đó một cách đệ quy ...).

Bạn có thể làm ví dụ đầu tiên của mình với d = {('apple', 'red') : 4}, nhưng sẽ rất khó để truy vấn những gì bạn muốn. Bạn cần phải làm điều gì đó như sau:

#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
Tôi đã không và sẽ không tán thành câu trả lời này, bởi vì ở quy mô lớn hơn, cơ sở dữ liệu (rõ ràng là!) Là cách tốt nhất để đi. Nhưng cảm giác ruột của tôi là một cơ sở dữ liệu là quá mức cần thiết cho nhu cầu của OP; có lẽ điều đó giải thích downvote?
gửi

4

Với các khóa dưới dạng bộ giá trị, bạn chỉ cần lọc các khóa có thành phần thứ hai nhất định và sắp xếp 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

Việc sắp xếp hoạt động vì các bộ giá trị có thứ tự tự nhiên nếu các thành phần của chúng có thứ tự tự nhiên.

Với các phím là các đối tượng chính thức, bạn chỉ cần lọc theo k.color == 'blue'.

Bạn thực sự không thể sử dụng dicts làm khóa, nhưng bạn có thể tạo một lớp đơn giản nhất như class Foo(object): passvà thêm bất kỳ thuộc tính nào vào nó một cách nhanh chóng:

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

Những trường hợp này có thể đóng vai trò là khóa dict, nhưng hãy cẩn thận với khả năng thay đổi của chúng!


3

Bạn có thể có một từ điển trong đó các mục nhập là danh sách các từ điển khác:

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

Đầu ra:

{'banana': [{'yellow': 24}], 'apple': [{'red': 12}, {'green': 14}]}

Chỉnh sửa: Như eumiro đã chỉ ra, bạn có thể sử dụng một kho từ điển:

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

Đầu ra:

{'banana': {'yellow': 24}, 'apple': {'green': 14, 'red': 12}}


2
Từ điển danh sách các từ điển? Có lẽ từ điển của từ điển sẽ đủ?
eumiro

@eumiro: Cảm ơn, bạn nói đúng, và đó là ý tưởng ban đầu của tôi. Tuy nhiên, tôi đã biến nó thành một chính tả danh sách các phái trong khi mã hóa ví dụ ban đầu. Tôi đã thêm một ví dụ về mệnh đề.
GreenMatt

Từ điển lồng nhau có xu hướng khó hiểu. Vui lòng xem câu trả lời của tôi
Cuga

@Cuga: Tôi đồng ý rằng việc phân chia các phái, v.v. có thể gây nhầm lẫn. Tôi chỉ cung cấp một ví dụ minh họa để trả lời câu hỏi của @ Nico như đã hỏi.
GreenMatt

Tôi xin lỗi: Tôi không cố ý ám chỉ giải pháp của bạn là sai; nó rõ ràng hoạt động và trong một số trường hợp, nó có thể là một trong những lý tưởng. Tôi muốn chia sẻ về tình huống của tôi.
Cuga

2

Loại dữ liệu này được lấy một cách hiệu quả từ cấu trúc dữ liệu giống như Trie. Nó cũng cho phép phân loại nhanh chóng. Mặc dù vậy, hiệu quả bộ nhớ có thể không tuyệt vời.

Một bộ ba truyền thống lưu trữ mỗi chữ cái của một từ dưới dạng một nút trong cây. Nhưng trong trường hợp của bạn, "bảng chữ cái" của bạn khác. Bạn đang lưu trữ chuỗi thay vì ký tự.

nó có thể trông giống như thế này:

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

xem liên kết này: trie trong python


2

Bạn muốn sử dụng hai khóa độc lập, vì vậy bạn có hai lựa chọn:

  1. Lưu trữ dự phòng dữ liệu với hai vùng là {'banana' : {'blue' : 4, ...}, .... }{'blue': {'banana':4, ...} ...}. Sau đó, việc tìm kiếm và sắp xếp rất dễ dàng nhưng bạn phải đảm bảo rằng bạn sửa đổi các phái với nhau.

  2. Lưu trữ nó chỉ một mệnh lệnh, và sau đó viết các hàm lặp lại chúng, ví dụ:

    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()]

Tôi không thể tìm ra lý do tại sao mã trong câu trả lời của tôi không hiển thị ở định dạng chính xác. Tôi đã thử chỉnh sửa và đánh dấu hai dòng cuối cùng là mã, nhưng nó không hoạt động!
highBandWidth

1
bạn đã tạo một danh sách được đánh số và trình phân tích cú pháp đang diễn giải mã (được thụt lề 4 khoảng trắng) là phần tiếp theo của mục thứ hai trong danh sách đó. Thụt lề mã thêm 4 dấu cách với tổng số là 8 và trình phân tích cú pháp sẽ nhận dạng mã đó là mã và định dạng chính xác.
gửi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.