Python中dict.clear()与分配{}之间的区别


167

在python中,调用clear()和分配{}给字典之间有区别吗?如果是,那是什么?例:

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


我想知道这是否在垃圾回收部分有所作为。我觉得.clear()应该对内存系统更好。
Xavier Nicollet

Answers:


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
谢谢。这是有道理的。我仍然必须习惯于=在python中创建引用的心态...
Marcin

15
=复制对名称的引用。python中没有变量,只有对象和名称。
tzot

17
虽然您的“无变量”声明在理论上是正确的,但在这里并没有真正的帮助。只要Python语言文档仍在谈论“变量”,我仍将使用该术语:docs.python.org/reference/datamodel.html
Greg Hewgill

9
我发现tzot的评论有助于调整我对名称,变量和副本类型的看法。称其为脚可能是您的意见,但我认为这是不公平的苛刻判断。
cfwschmidt 2014年

1
同样clear()不会破坏字典中可能仍然被其他人引用的移除对象。
罗伦佐·贝利

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为空,因此这并不是对所有情况均有效的速度测试。我认为做出较大的决定(或至少包含一些内容)会产生较小的性能差异……再加上我怀疑垃圾收集器可能会对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
我不确定差异是否显着。无论如何,在我的机器上,结果是相反的!
阿里斯蒂德'02

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使用很多方法可能不是最好的方法。
惊人的2014年

3
@fantabolous使用global将使函数的行为有所不同-所有对conf_decorator的调用都将共享相同的c_kwargs变量。我相信Python 3添加了nonlocal关键字来解决此问题,并且可以正常工作。
Ponkadoodle

1

另外,有时dict实例可能是dict的子类(defaultdict例如)。在这种情况下,clear首选使用using ,因为我们不必记住dict的确切类型,并且还避免重复代码(将清除行与初始化行耦合)。

x = defaultdict(list)
x[1].append(2)
...
x.clear() # instead of the longer x = defaultdict(list)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.