ความแตกต่างระหว่าง dict.clear () และการกำหนด {} ใน Python


167

ในหลามมีความแตกต่างระหว่างการโทรclear()และการกำหนดให้{}กับพจนานุกรมหรือไม่ ถ้าใช่มันคืออะไร ตัวอย่าง:

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


ฉันสงสัยว่าสิ่งนี้สร้างความแตกต่างในส่วนของการเก็บขยะหรือไม่ ฉันรู้สึกเหมือน. ชัดเจน () ควรดีกว่าระบบหน่วยความจำ
Xavier Nicollet

คำตอบ:


285

หากคุณมีตัวแปรอื่นที่อ้างถึงพจนานุกรมเดียวกันก็มีความแตกต่างใหญ่:

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

นี่เป็นเพราะการกำหนดd = {}สร้างพจนานุกรมใหม่ที่ว่างเปล่าและกำหนดให้กับdตัวแปร ใบนี้จะd2ชี้ไปที่พจนานุกรมเก่าโดยที่ยังมีรายการอยู่ อย่างไรก็ตามd.clear()ล้างพจนานุกรมเดียวกันกับที่dและd2ทั้งคู่ชี้ไปที่


7
ขอบคุณ มันสมเหตุสมผลแล้ว ฉันยังคงมีที่จะได้ใช้ความคิดที่จะสร้าง = อ้างอิงในหลาม ...
Marcin

15
= คัดลอกการอ้างอิงไปยังชื่อ ไม่มีตัวแปรในไพ ธ อนเฉพาะวัตถุและชื่อ
tzot

17
ในขณะที่คำสั่ง "ไม่มีตัวแปร" ของคุณเป็นจริงอวดรู้มันไม่เป็นประโยชน์จริง ๆ ที่นี่ ตราบใดที่เอกสารภาษา Python ยังคงพูดถึง "ตัวแปร" ฉันจะยังคงใช้คำว่า: docs.python.org/reference/datamodel.html
Greg Hewgill

9
ฉันพบความคิดเห็นของ tzot มีประโยชน์ในการปรับความคิดเกี่ยวกับชื่อตัวแปรและประเภทของสำเนา อาจจะเป็นความคิดเห็นของคุณ แต่ฉันคิดว่ามันเป็นการตัดสินที่ไม่ยุติธรรม
cfwschmidt

1
ชัดเจน () อย่าทำลายวัตถุที่ถูกลบออกใน dict ซึ่งอาจถูกอ้างอิงโดยบุคคลอื่น
Lorenzo Belli

31

d = {}จะสร้างอินสแตนซ์ใหม่สำหรับdแต่การอ้างอิงอื่น ๆ ทั้งหมดจะยังคงชี้ไปที่เนื้อหาเก่า d.clear()จะรีเซ็ตเนื้อหา แต่การอ้างอิงทั้งหมดไปยังอินสแตนซ์เดียวกันจะยังคงถูกต้อง


21

นอกเหนือจากความแตกต่างที่กล่าวถึงในคำตอบอื่น ๆ แล้วยังมีความแตกต่างความเร็ว d = {} เร็วกว่าสองเท่า:

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
นี่ไม่ใช่การทดสอบความเร็วที่ถูกต้องสำหรับทุกกรณีเนื่องจาก dict ว่างเปล่า ฉันคิดว่าการทำ dict ครั้งใหญ่ (หรืออย่างน้อยก็เนื้อหาบางส่วน) จะให้ผลการทำงานที่แตกต่างกันเล็กน้อย ... และฉันคิดว่าตัวเก็บขยะอาจเพิ่มความเสียหายเล็กน้อยให้กับ d = {} (?)
Rafe

3
@Rafe: ฉันคิดว่าประเด็นคือถ้าเรารู้ว่าไม่มีตัวแปรอื่นที่ชี้ไปที่พจนานุกรม d ดังนั้นการตั้งค่าd = {}ควรเร็วขึ้นเนื่องจากการล้างข้อมูลทั้งหมดสามารถทิ้งไว้ที่ Garbage Collector ได้ในภายหลัง
ViFI

8

ในฐานะที่เป็นภาพประกอบสำหรับสิ่งที่กล่าวถึงก่อนหน้านี้:

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

สิ่งนี้แสดงให้เห็นว่า.clearปรับเปลี่ยนวัตถุ แต่ `= {} 'สร้างวัตถุใหม่
wizzwizz4

7

นอกเหนือจากคำตอบของ @odano ดูเหมือนว่าการใช้d.clear()จะเร็วขึ้นหากคุณต้องการล้างคำสั่งหลายครั้ง

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)

ผลลัพธ์คือ:

20.0367929935
19.6444659233

4
ฉันไม่แน่ใจว่าความแตกต่างมีนัยสำคัญ อย่างไรก็ตามบนเครื่องของฉันผลลัพธ์ตรงกันข้าม!
Aristide

7

วิธีการกลายพันธุ์จะมีประโยชน์เสมอหากวัตถุต้นฉบับไม่อยู่ในขอบเขต:

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

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

การกำหนดพจนานุกรมใหม่จะสร้างวัตถุใหม่และจะไม่แก้ไขต้นฉบับ


4

สิ่งหนึ่งที่ไม่ได้กล่าวถึงคือการกำหนดขอบเขต ไม่ใช่ตัวอย่างที่ดี แต่นี่เป็นกรณีที่ฉันพบปัญหา:

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

การแก้ปัญหาคือการแทนที่c_kwargs = {}ด้วยc_kwargs.clear()

หากใครบางคนคิดตัวอย่างที่เป็นประโยชน์ได้มากขึ้นอย่าลังเลที่จะแก้ไขโพสต์นี้


global c_kwargsอาจจะไม่ทำงานหรือไม่ แม้ว่าอาจglobalไม่ใช่สิ่งที่ดีที่สุดที่จะใช้บ่อยๆ
เพ้อฝัน

3
@fantabolous การใช้globalจะทำให้ฟังก์ชั่นทำงานแตกต่างกัน - การเรียกไปที่ conf_decorator ทั้งหมดจะแบ่งปันตัวแปร c_kwargs เดียวกัน ฉันเชื่อว่า Python 3 เพิ่มnonlocalคำหลักเพื่อแก้ไขปัญหานี้และสิ่งนี้จะใช้ได้
Ponkadoodle

1

นอกจากนี้บางครั้งอินสแตนซ์ dict อาจเป็นคลาสย่อยของ dict ( defaultdictตัวอย่าง) ในกรณีดังกล่าวclearเราแนะนำให้ใช้เพราะเราไม่จำเป็นต้องจำชนิด dict ที่แน่นอนและยังหลีกเลี่ยงรหัสที่ซ้ำกัน

x = defaultdict(list)
x[1].append(2)
...
x.clear() # instead of the longer x = defaultdict(list)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.