2013-04-14 40 views
11

Tôi có một dict đa chiều, và tôi muốn có thể lấy một giá trị bằng một khóa: cặp khóa và trả về 'NA' nếu khóa đầu tiên không tồn tại . Tất cả các sub-dicts đều có cùng khóa.Python dict.get() với dict đa chiều

d = { 'a': {'j':1,'k':2}, 
     'b': {'j':2,'k':3}, 
     'd': {'j':1,'k':3} 
    } 

Tôi biết tôi có thể sử dụng d.get('c','NA') để có được những phụ dict nếu nó tồn tại và trở lại 'NA' khác, nhưng tôi thực sự chỉ cần một giá trị từ các tiểu dict. Tôi muốn làm một cái gì đó như d.get('c['j']','NA') nếu có.

Hiện tại, tôi chỉ kiểm tra xem khóa cấp cao nhất có tồn tại không và sau đó gán giá trị phụ cho biến nếu nó tồn tại hoặc 'NA' nếu không. Tuy nhiên, tôi đang làm điều này khoảng 500k lần và cũng lấy/tạo ra thông tin khác về mỗi khóa cấp cao nhất từ ​​nơi khác, và tôi đang cố gắng tăng tốc độ này lên một chút.

Trả lời

20

Làm thế nào về

d.get('a', {'j': 'NA'})['j'] 

?

Nếu không phải tất cả subdicts có một chìa khóa j, sau đó

d.get('a', {}).get('j', 'NA') 

 

Để giảm bớt đối tượng giống hệt nhau tạo ra, bạn có thể nghĩ ra một cái gì đó giống như

class DefaultNASubdict(dict): 
    class NADict(object): 
     def __getitem__(self, k): 
      return 'NA' 

    NA = NADict() 

    def __missing__(self, k): 
     return self.NA 

nadict = DefaultNASubdict({ 
       'a': {'j':1,'k':2}, 
       'b': {'j':2,'k':3}, 
       'd': {'j':1,'k':3} 
      }) 

print nadict['a']['j'] # 1 
print nadict['b']['j'] # 2 
print nadict['c']['j'] # NA 

 

Cùng một ý tưởng sử dụng defaultdict:

import collections 

class NADict(object): 
    def __getitem__(self, k): 
     return 'NA' 

    @staticmethod 
    def instance(): 
     return NADict._instance 

NADict._instance = NADict() 


nadict = collections.defaultdict(NADict.instance, { 
       'a': {'j':1,'k':2}, 
       'b': {'j':2,'k':3}, 
       'd': {'j':1,'k':3} 
      }) 
+0

nhìn vào 'collections.defaultdict' cho việc thực hiện đã được cung cấp, ví dụ:' defaultdict (lambda: defaultdict (lambda: 'NA')) ' – mtadd

+0

Chắc chắn, nhưng bạn vẫn cần một' NADict' và một hàm trả về một cá thể chia sẻ của nó. Tôi sẽ thêm một ví dụ. –

+0

@mtadd: ý tưởng là không tạo ra một dict mới/defaultdict trên mọi lần tra cứu. –

2

Thay vì một hệ thống các lồng dict đối tượng, bạn có thể sử dụng một từ điển có phím là một tuple đại diện cho một con đường thông qua hệ thống phân cấp.

In [34]: d2 = {(x,y):d[x][y] for x in d for y in d[x]} 

In [35]: d2 
Out[35]: 
{('a', 'j'): 1, 
('a', 'k'): 2, 
('b', 'j'): 2, 
('b', 'k'): 3, 
('d', 'j'): 1, 
('d', 'k'): 3} 

In [36]: timeit [d[x][y] for x,y in d2.keys()] 
100000 loops, best of 3: 2.37 us per loop 

In [37]: timeit [d2[x] for x in d2.keys()] 
100000 loops, best of 3: 2.03 us per loop 

Truy cập theo cách này có vẻ nhanh hơn khoảng 15%. Bạn vẫn có thể sử dụng phương pháp get với một giá trị mặc định:

In [38]: d2.get(('c','j'),'NA') 
Out[38]: 'NA' 
4

Đây là một cách đơn giản và hiệu quả để làm điều đó với các từ điển thông thường, lồng một số tùy ý các cấp:

d = {'a': {'j': 1, 'k': 2}, 
    'b': {'j': 2, 'k': 3}, 
    'd': {'j': 1, 'k': 3}, 
    } 

def chained_get(dct, *keys): 
    SENTRY = object() 
    def getter(level, key): 
     return 'NA' if level is SENTRY else level.get(key, SENTRY) 
    return reduce(getter, keys, dct) 

print chained_get(d, 'a', 'j') # 1 
print chained_get(d, 'b', 'k') # 3 
print chained_get(d, 'k', 'j') # NA 

Nó cũng có thể là thực hiện đệ quy:

def chained_get(dct, *keys): 
    SENTRY = object() 
    def getter(level, keys): 
     return (level if keys[0] is SENTRY else 
        'NA' if level is SENTRY else 
         getter(level.get(keys[0], SENTRY), keys[1:])) 
    return getter(dct, keys+(SENTRY,)) 

Mặc dù cách làm như vậy không hiệu quả như trước đây.

0

Một cách khác để có được đa chiều dụ dict (sử dụng phương pháp get hai lần)

d.get('a', {}).get('j') 
Các vấn đề liên quan