2011-08-22 36 views
10

Hãy nói rằng tôi có mã này:dict.get() phương thức trả về một con trỏ

my_dict = {} 
default_value = {'surname': '', 'age': 0} 

# get info about john, or a default dict 
item = my_dict.get('john', default_value) 

# edit the data 
item[surname] = 'smith' 
item[age] = 68 

my_dict['john'] = item 

Vấn đề trở nên rõ ràng, nếu bây giờ chúng tôi kiểm tra giá trị của DEFAULT_VALUE:

>>> default_value 
{'age': 68, 'surname': 'smith'} 

Rõ ràng , rằng my_dict.get() không trả về giá trị của default_value, nhưng con trỏ (?) cho nó.

Vấn đề có thể được giải quyết bằng cách thay đổi mã để:

item = my_dict.get('john', {'surname': '', 'age': 0}) 

nhưng điều đó dường như không phải là một cách tốt đẹp để làm điều đó. Bất kỳ ý kiến, ý kiến?

Trả lời

16
item = my_dict.get('john', default_value.copy()) 

Bạn đang luôn chuyển tham chiếu bằng Python.

này không quan trọng cho các đối tượng bất biến như str, int, tuple vv kể từ khi bạn không thể thay đổi, chỉ trỏ một tên tại một đối tượng khác nhau, nhưng nó cho các đối tượng có thể thay đổi như list, set, và dict. Bạn cần phải làm quen với điều này và luôn ghi nhớ điều đó.

Chỉnh sửa: Zach Bloom và Jonathan Sternberg cả hai chỉ ra các phương pháp bạn có thể sử dụng để tránh cuộc gọi đến copy trên mọi lần tra cứu. Bạn nên sử dụng một trong hai phương pháp defaultdict, một cái gì đó giống như phương pháp đầu tiên của Jonathan, hoặc:

def my_dict_get(key): 
    try: 
     item = my_dict[key] 
    except KeyError: 
     item = default_value.copy() 

Đây sẽ là nhanh hơn so với if khi phím gần như luôn luôn đã tồn tại trong my_dict, nếu dict là lớn. Bạn không cần phải bọc nó trong một chức năng nhưng bạn có thể không muốn bốn dòng đó mỗi lần bạn truy cập my_dict.

Xem câu trả lời của Jonathan về thời gian với số dict nhỏ. Phương pháp get hoạt động kém ở mọi kích cỡ tôi đã thử nghiệm, nhưng phương pháp try hoạt động tốt hơn ở kích thước lớn.

+1

Đây là một nguyên lý rất quan trọng của python - * tất cả các giá trị được truyền * bằng cách tham chiếu. Tính đột biến của các tham chiếu đó là một vấn đề hoàn toàn khác (mặc dù nó thường đi theo con người theo cách này). –

+1

Tôi chắc chắn rằng tôi đã đọc về nó trước đây, nhưng bạn có xu hướng quên đi những điều khi không sử dụng một ngôn ngữ trong một thời gian dài. Cảm ơn bạn đã làm rõ điều đó. – Armandas

+0

Tại sao câu trả lời của bạn khác với câu trả lời được cung cấp trong câu hỏi. Câu hỏi có vẻ là nhiều hơn về việc tìm kiếm một cách thanh lịch để trả lại một trường hợp mới của dict, nhưng chỉ tạo ra nó khi cần thiết. – Dunes

7

Trong Python dicts là cả hai đối tượng (vì vậy chúng luôn được truyền dưới dạng tham chiếu) và có thể thay đổi (nghĩa là chúng có thể thay đổi mà không được tạo lại).

Bạn có thể sao chép từ điển của bạn mỗi khi bạn sử dụng nó:

my_dict.get('john', default_value.copy()) 

Bạn cũng có thể sử dụng bộ sưu tập defaultdict:

from collections import defaultdict 

def factory(): 
    return {'surname': '', 'age': 0} 

my_dict = defaultdict(factory) 

my_dict['john'] 
8

Không sử dụng được. Bạn có thể làm:

item = my_dict.get('john', default_value.copy()) 

Nhưng điều này đòi hỏi phải có một cuốn từ điển để được sao chép ngay cả khi mục từ điển tồn tại. Thay vào đó, hãy xem xét chỉ kiểm tra xem giá trị có ở đó không.

item = my_dict['john'] if 'john' in my_dict else default_value.copy() 

Vấn đề duy nhất với điều này là nó sẽ thực hiện hai lần tra cứu cho 'john' thay vì chỉ một. Nếu bạn sẵn sàng sử dụng một dòng phụ (và Không có giá trị nào bạn có thể nhận được từ từ điển), bạn có thể làm:

item = my_dict.get('john') 
if item is None: 
    item = default_value.copy() 

EDIT: Tôi nghĩ mình sẽ so sánh tốc độ với thời gian . Các default_value và my_dict là globals. Tôi đã làm cho họ cho cả hai nếu chìa khóa đã có, và nếu có một bỏ lỡ.

Sử dụng ngoại lệ:

def my_dict_get(): 
    try: 
     item = my_dict['key'] 
    except KeyError: 
     item = default_value.copy() 

# key present: 0.4179 
# key absent: 3.3799 

Dùng get và kiểm tra nếu nó None.

def my_dict_get(): 
    item = my_dict.get('key') 
    if item is None: 
     item = default_value.copy() 

# key present: 0.57189 
# key absent: 0.96691 

Kiểm tra sự tồn tại của nó với các đặc biệt if/else Cú pháp

def my_dict_get(): 
    item = my_dict['key'] if 'key' in my_dict else default_value.copy() 

# key present: 0.39721 
# key absent: 0.43474 

ngây thơ sao chép từ điển.

def my_dict_get(): 
    item = my_dict.get('key', default_value.copy()) 

# key present: 0.52303 (this may be lower than it should be as the dictionary I used was one element) 
# key absent: 0.66045 

Phần lớn mọi thứ ngoại trừ trường hợp ngoại lệ đều rất giống nhau. Cú pháp if/else đặc biệt dường như có thời gian thấp nhất vì một lý do nào đó (không biết tại sao).

+0

Đây là một tốt, tôi sẽ thêm một ghi chú vào câu trả lời của tôi. Làm thế nào về''john 'trong my_dict' thay vì 'my_dict.has_key (' john ')' và 'my_dict.get (' john ')' thay vì 'my_dict.get (' john ', None)'? – agf

+0

Tôi thích sử dụng tốt hơn has_key. Tôi quên rằng đã tồn tại. Tôi không biết rằng my_dict.get ('john') mặc định trả về null (tôi nghĩ đó là một IndexError). –

+0

Hoặc sử dụng điều này: từ các bộ sưu tập nhập mặc địnhdict mydict = defaultdict (default_value.copy) Sau đó, khi bạn làm mydict [key-thats-not-here], hàm bạn truyền cho hàm tạo sẽ được gọi. –

2

Điều chính để nhận ra là mọi thứ bằng Python là tham chiếu chéo. Một tên biến trong ngôn ngữ kiểu C thường viết tắt cho vùng bộ nhớ hình dạng đối tượng và gán cho biến đó tạo bản sao của một khu vực hình đối tượng khác ... trong Python, biến chỉ là các từ khóa trong từ điển (locals())) và hành động giao chỉ lưu trữ một tham chiếu mới. (Về mặt kỹ thuật, mọi thứ là một con trỏ, nhưng đó là một chi tiết triển khai).

Điều này có một số ý nghĩa, bản chính sẽ không bao giờ là bản sao ngầm của đối tượng được tạo bởi vì bạn đã chuyển nó tới hàm, gán nó, vv Cách duy nhất để có một bản sao là vì thế. Các stdlib python cung cấp một mô-đun copy có chứa một số thứ, bao gồm một hàm copy()deepcopy() khi bạn muốn tạo một bản sao của một cái gì đó một cách rõ ràng. Ngoài ra, một số loại phơi bày hàm .copy() của riêng chúng, nhưng đây không phải là một tiêu chuẩn hoặc được triển khai nhất quán. Những người khác không thay đổi có xu hướng đôi khi cung cấp một phương pháp .replace(), mà làm cho một bản sao đột biến.


Trong trường hợp mã của bạn, chuyển trường hợp ban đầu rõ ràng không hoạt động và tạo bản sao trước (khi bạn không cần) sẽ lãng phí. Vì vậy, giải pháp đơn giản nhất có lẽ là ...

item = my_dict.get('john') 
if item is None: 
    item = default_dict.copy() 

Nó sẽ hữu ích trong trường hợp này nếu .get() được hỗ trợ thông qua trong một hàm constructor giá trị mặc định, nhưng đó có lẽ quá kỹ thuật một lớp cơ sở cho một trường hợp biên giới.

1

my_dict.get('john', default_value.copy()) sẽ tạo ra một bản sao của mặc định dict mỗi lần get được gọi là (ngay cả khi 'john' là hiện tại và trả lại), nó là nhanh hơn và rất OK để sử dụng này thử/trừ tùy chọn:

try: 
    return my_dict['john'] 
except KeyError: 
    return {'surname': '', 'age': 0} 

Ngoài ra, bạn cũng có thể sử dụng một defaultdict:

import collections 

def default_factory(): 
    return {'surname': '', 'age': 0} 

my_dict = collections.defaultdict(default_factory) 
Các vấn đề liên quan