Pythonでのdict.clear()と{}の割り当ての違い


167

Pythonでは、呼び出しclear(){}辞書への割り当てに違いはありますか?はいの場合、それは何ですか?例:

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


これは、ガベージコレクションの部分に違いがあるのでしょうか。.clear()の方がメモリシステムに適していると思います。
ザビエルニコレット2018年

回答:


285

同じ辞書を参照する別の変数がある場合、大きな違いがあります。

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

これは、割り当てd = {}によって新しい空の辞書が作成され、d変数に割り当てられるためです。これによりd2、アイテムがまだ残っている古い辞書を指しています。しかし、d.clear()その同じ辞書クリアdd2の両方の点では。


7
ありがとう。意味あり。私はまだ= Pythonで参照を作成するという考え方に慣れる必要があります...
Marcin

15
=名前への参照をコピーします。Pythonには変数はなく、オブジェクトと名前のみが含まれます。
tzot 2008

17
「変数なし」ステートメントは明確に真実ですが、ここではあまり役に立ちません。Python言語のドキュメントが「変数」について語っている限り、私は次の用語を引き続き使用します:docs.python.org/reference/datamodel.html
Greg Hewgill

9
tzotのコメントは、名前、変数、コピーの種類に関する私の考えを調整するのに役立ちました。それを「奇抜」と呼ぶのはあなたの意見かもしれませんが、私はそれを不当に厳しい判断だと思います。
cfwschmidt 2014

1
また、clear()は、他の誰かがまだ参照している可能性があるdict内の削除されたオブジェクトを破棄しません。
Lorenzo Belli 2017年

31

d = {}は新しいインスタンスを作成しますdが、他のすべての参照は引き続き古いコンテンツを指します。 d.clear()は内容をリセットしますが、同じインスタンスへのすべての参照は引き続き正しくなります。


21

他の回答で述べられている違いに加えて、速度の違いもあります。d = {}は2倍の速さです。

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
辞書が空であるため、これは実際にはすべてのケースに対して有効な速度テストではありません。大きな辞書(または少なくとも一部のコンテンツ)を作成すると、パフォーマンスの差がはるかに小さくなると思います...さらに、ガベージコレクターがd = {}(?)
Rafe

3
@Rafe:他の変数がディクショナリdを指していないことがわかっている場合にポイントを考えます。d = {}全体のクリーンアップは後でガベージコレクターに任せることができるため、設定はより速くなるはずです。
ViFI 2016年

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
違いが重要であるかどうかはわかりません。とにかく、私のマシンでは、結果は逆です!
2016

7

変異元のオブジェクトがスコープ内にない場合の方法は、常に便利です。

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

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

辞書を再割り当てすると、新しいオブジェクトが作成され、元のオブジェクトは変更されません。


4

言及されていないことの1つはスコープの問題です。良い例ではありませんが、ここで私が問題に遭遇した場合です:

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、多くを使用するのが最善ではありません。
14

3
@fantabolousを使用globalすると、関数の動作が異なります。conf_decoratorへのすべての呼び出しは、同じc_kwargs変数を共有します。Python 3がnonlocalこの問題に対処するためのキーワードを追加したと思います。
ポンカドゥードル2014

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.