Sự khác biệt giữa dict.clear () và gán {} trong Python


167

Trong python, có sự khác biệt giữa gọi clear()và gán {}cho một từ điển? Nếu có, nó là cái gì? Thí dụ:

d = {"stuff":"things"}
d.clear()   #this way
d = {}      #vs this way


Tôi tự hỏi nếu điều này làm cho một sự khác biệt về phần thu gom rác. Tôi cảm thấy như .clear () sẽ đẹp hơn với hệ thống bộ nhớ.
Xavier Nicollet

Câu trả lời:


285

Nếu bạn có một biến khác cũng đề cập đến cùng một từ điển, có một sự khác biệt lớn:

>>> d = {"stuff": "things"}
>>> d2 = d
>>> d = {}
>>> d2
{'stuff': 'things'}
>>> d = {"stuff": "things"}
>>> d2 = d
>>> d.clear()
>>> d2
{}

Điều này là do việc gán d = {}tạo ra một từ điển mới, trống và gán nó cho dbiến. Lá này d2chỉ vào từ điển cũ với các mục vẫn còn trong đó. Tuy nhiên, d.clear()xóa cùng một từ điển dd2cả hai điểm.


7
Cảm ơn. Điều này thật ý nghĩa. Tôi vẫn phải làm quen với suy nghĩ rằng = tạo tài liệu tham khảo trong python ...
Marcin

15
= sao chép tham chiếu đến tên. Không có biến trong python, chỉ có các đối tượng và tên.
tzot

17
Mặc dù tuyên bố "không có biến" của bạn là đúng về mặt giáo dục, nhưng nó không thực sự hữu ích ở đây. Miễn là tài liệu ngôn ngữ Python vẫn nói về "các biến", tôi vẫn sẽ sử dụng thuật ngữ: docs.python.org/reference/datamodel.html
Greg Hewgill

9
Tôi thấy nhận xét của tzot hữu ích trong việc điều chỉnh suy nghĩ của tôi về tên, biến và loại bản sao. Gọi nó là pedantic có thể là ý kiến ​​của bạn, nhưng tôi thấy đó là một bản án khắc nghiệt không công bằng.
cfwschmidt

1
Cũng rõ ràng () không phá hủy đối tượng bị loại bỏ trong dict mà vẫn có thể được tham chiếu bởi người khác.
Lorenzo Belli

31

d = {}sẽ tạo một phiên bản mới cho dnhưng tất cả các tham chiếu khác vẫn sẽ trỏ đến nội dung cũ. d.clear()sẽ đặt lại nội dung, nhưng tất cả các tham chiếu đến cùng một thể hiện vẫn sẽ chính xác.


21

Ngoài sự khác biệt được đề cập trong các câu trả lời khác, còn có sự khác biệt về tốc độ. d = {} nhanh hơn gấp đôi:

python -m timeit -s "d = {}" "for i in xrange(500000): d.clear()"
10 loops, best of 3: 127 msec per loop

python -m timeit -s "d = {}" "for i in xrange(500000): d = {}"
10 loops, best of 3: 53.6 msec per loop

9
Đây thực sự không phải là một bài kiểm tra tốc độ hợp lệ cho tất cả các trường hợp vì dict trống. Tôi nghĩ rằng việc tạo ra một lệnh lớn (hoặc ít nhất là một số nội dung) sẽ mang lại sự khác biệt hiệu suất nhỏ hơn nhiều ... cộng với tôi nghi ngờ người thu gom rác có thể gây thêm một chút tổn thương cho d = {} (?)
Rafe

3
@Rafe: Tôi nghĩ vấn đề là nếu chúng ta biết rằng không có biến nào khác đang trỏ đến từ điển d, thì việc cài đặt d = {}sẽ nhanh hơn vì việc dọn dẹp toàn bộ có thể được để lại cho Garbage Collector sau này.
ViFI

8

Như một minh họa cho những điều đã được đề cập trước đó:

>>> a = {1:2}
>>> id(a)
3073677212L
>>> a.clear()
>>> id(a)
3073677212L
>>> a = {}
>>> id(a)
3073675716L

Điều này cho thấy rằng .clearsửa đổi đối tượng nhưng `= {}` tạo ra một đối tượng mới.
wizzwizz4

7

Ngoài câu trả lời của @odano, có vẻ như việc sử dụng d.clear()sẽ nhanh hơn nếu bạn muốn xóa chính tả nhiều lần.

import timeit

p1 = ''' 
d = {}
for i in xrange(1000):
    d[i] = i * i
for j in xrange(100):
    d = {}
    for i in xrange(1000):
        d[i] = i * i
'''

p2 = ''' 
d = {}
for i in xrange(1000):
    d[i] = i * i
for j in xrange(100):
    d.clear()
    for i in xrange(1000):
        d[i] = i * i
'''

print timeit.timeit(p1, number=1000)
print timeit.timeit(p2, number=1000)

Kết quả là:

20.0367929935
19.6444659233

4
Tôi không chắc sự khác biệt là có ý nghĩa. Dù sao, trên máy của tôi, kết quả là ngược lại!
Aristide

7

Các phương thức gây đột biến luôn hữu ích nếu đối tượng ban đầu không nằm trong phạm vi:

def fun(d):
    d.clear()
    d["b"] = 2

d={"a": 2}
fun(d)
d          # {'b': 2}

Việc gán lại từ điển sẽ tạo ra một đối tượng mới và sẽ không sửa đổi đối tượng ban đầu.


4

Một điều không được đề cập là vấn đề phạm vi. Không phải là một ví dụ tuyệt vời, nhưng đây là trường hợp tôi gặp phải vấn đề:

def conf_decorator(dec):
    """Enables behavior like this:
        @threaded
        def f(): ...

        or

        @threaded(thread=KThread)
        def f(): ...

        (assuming threaded is wrapped with this function.)
        Sends any accumulated kwargs to threaded.
        """
    c_kwargs = {}
    @wraps(dec)
    def wrapped(f=None, **kwargs):
        if f:
            r = dec(f, **c_kwargs)
            c_kwargs = {}
            return r
        else:
            c_kwargs.update(kwargs) #<- UnboundLocalError: local variable 'c_kwargs' referenced before assignment
            return wrapped
    return wrapped

Giải pháp là thay thế c_kwargs = {}bằngc_kwargs.clear()

Nếu ai đó nghĩ ra một ví dụ thực tế hơn, vui lòng chỉnh sửa bài đăng này.


global c_kwargscó lẽ cũng sẽ làm việc không? Mặc dù có lẽ globalkhông phải là thứ tốt nhất để sử dụng nhiều.
tưởng tượng

3
@fantabolous sử dụng globalsẽ làm cho hàm hoạt động khác đi - tất cả các lệnh gọi tới conf_decorator sau đó sẽ chia sẻ cùng một biến c_kwargs. Tôi tin rằng Python 3 đã thêm nonlocaltừ khóa để giải quyết vấn đề này và nó sẽ hoạt động.
Ponkadoodle

1

Ngoài ra, đôi khi thể hiện dict có thể là một lớp con của dict ( defaultdictví dụ). Trong trường hợp đó, việc sử dụng clearđược ưu tiên hơn, vì chúng ta không phải nhớ chính xác loại chính tả và cũng tránh mã trùng lặp (ghép dòng xóa với dòng khởi tạo).

x = defaultdict(list)
x[1].append(2)
...
x.clear() # instead of the longer x = defaultdict(list)
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.