2010-06-08 41 views
17

Tôi muốn khởi tạo từ điển các bộ (bằng Python 2.6) bằng cách sử dụng dict.fromkeys, nhưng cấu trúc kết quả hoạt động lạ lùng. Cụ thể hơn:Hành vi không mong muốn từ dict.fromkeys

>>>> x = {}.fromkeys(range(10), set([])) 
>>>> x 
{0: set([]), 1: set([]), 2: set([]), 3: set([]), 4: set([]), 5: set([]), 6: set([]), 7: set([]), 8: set([]), 9: set([])} 
>>>> x[5].add(3) 
>>>> x 
{0: set([3]), 1: set([3]), 2: set([3]), 3: set([3]), 4: set([3]), 5: set([3]), 6: set([3]), 7: set([3]), 8: set([3]), 9: set([3])} 

Tôi rõ ràng không muốn thêm 3 vào tất cả các bộ, chỉ vào tập hợp tương ứng với x[5]. Tất nhiên, tôi có thể tránh được vấn đề bằng cách khởi tạo x mà không cần fromkeys, nhưng tôi muốn hiểu những gì tôi đang thiếu ở đây.

+4

Chúng đều là cùng một tập hợp. Bộ, danh sách, từ điển và bất kỳ đối tượng nào khác là loại tham chiếu và khi bạn chỉ định chúng cho biến khác, chỉ tham chiếu được sao chép chứ không phải đối tượng thực. 'fromkeys' phải sử dụng gán để kết hợp tập hợp với mỗi khóa, nhưng như bạn thấy, điều này không sao chép tập hợp. Tôi không chắc chắn làm thế nào để có được xung quanh này, ngoài việc tạo ra các từ điển theo một cách khác nhau. –

Trả lời

14

Đối số thứ hai cho dict.fromkeys chỉ là một giá trị. Bạn đã tạo một từ điển có cùng một đặt làm giá trị cho mỗi khóa. Có lẽ bạn hiểu cách thức hoạt động:

>>> a = set() 
>>> b = a 
>>> b.add(1) 
>>> b 
set([1]) 
>>> a 
set([1]) 

bạn đang thấy cùng một hành vi ở đó; trong trường hợp của bạn, x[0], x[1], x[2] (v.v) là tất cả các cách khác nhau để truy cập chính xác đối tượng set.

Đây là một chút dễ dàng hơn để xem với các đối tượng mà đại diện chuỗi bao gồm địa chỉ bộ nhớ của họ, nơi bạn có thể thấy rằng họ đang giống hệt nhau:

>>> dict.fromkeys(range(2), object()) 
{0: <object object at 0x1001da080>, 
1: <object object at 0x1001da080>} 
0

Lý do của nó làm việc theo cách này là set([]) tạo ra một đối tượng (một đối tượng đã đặt). Từ đó, các con trỏ sử dụng đối tượng cụ thể đó để tạo tất cả các mục từ điển của nó. Hãy xem xét:

>>> x 
{0: set([]), 1: set([]), 2: set([]), 3: set([]), 4: set([]), 5: set([]), 
6: set([]), 7: set([]), 8: set([]), 9: set([])} 
>>> x[0] is x[1] 
True 

Tất cả các bộ đều giống nhau!

+1

Bạn thực sự nên so sánh danh tính: 'x [0] là x [1]'. –

3

Do this từ dictobject.c:

while (_PyDict_Next(seq, &pos, &key, &oldvalue, &hash)) 
{ 
      Py_INCREF(key); 
      Py_INCREF(value); 
      if (insertdict(mp, key, hash, value)) 
       return NULL; 
} 

Các value là bạn "set ([])", nó được đánh giá chỉ một lần thì kết quả đếm tham chiếu đối tượng của họ được tăng lên và thêm vào từ điển, nó không đánh giá nó mỗi khi nó thêm vào dict.

0

#To do what you want: 

import copy 
s = set([]) 
x = {} 
for n in range(0,5): 
    x[n] = copy.deepcopy(s) 
x[2].add(3) 
print x 

#Printing 
#{0: set([]), 1: set([]), 2: set([3]), 3: set([]), 4: set([])} 
+2

Không cần 'deepcopy'. 'x [n] = set()' tạo một tập mới cho mỗi giá trị. –

13

Bạn có thể làm điều này với một biểu thức máy phát điện:

x = dict((i,set()) for i in range(10)) 

Trong Python 3, bạn có thể sử dụng một sự hiểu biết từ điển:

x = { i : set() for i in range(10) } 

Trong cả hai trường hợp, khái niệm set() được đánh giá cho mỗi phần tử, thay vì được đánh giá một lần và được sao chép vào từng phần tử.

+0

Được rồi, cảm ơn! –

+1

+1 để cung cấp giải pháp ngay cả sau khi câu trả lời được chấp nhận giải thích rõ. – Randy

+0

Nếu thay vì đặt tôi muốn khởi tạo danh sách, x = {i: [] cho i trong phạm vi (10)} gây ra Cú pháp Cú pháp trong khi lệnh dict ((i, []) cho i trong phạm vi (10)) thì không. – Eduardo

Các vấn đề liên quan