programing tip

사전을 복사하고 사본 만 편집하는 방법

itbloger 2020. 9. 30. 08:53
반응형

사전을 복사하고 사본 만 편집하는 방법


누군가 나에게 이것을 설명해 주시겠습니까? 이것은 나에게 의미가 없습니다.

사전을 다른 사전으로 복사하고 두 번째를 편집하면 둘 다 변경됩니다. 왜 이런 일이 발생합니까?

>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = dict1
>>> dict2
{'key2': 'value2', 'key1': 'value1'}
>>> dict2["key2"] = "WHY?!"
>>> dict1
{'key2': 'WHY?!', 'key1': 'value1'}

파이썬은 결코 암시 적으로 객체를 복사 하지 않습니다 . 을 설정하면 dict2 = dict1정확히 동일한 dict 객체를 참조하게되므로 변경하면 해당 객체에 대한 모든 참조가 현재 상태의 객체를 계속 참조합니다.

드물게 dict를 복사하려면 다음을 사용하여 명시 적으로 수행해야합니다.

dict2 = dict(dict1)

또는

dict2 = dict1.copy()

당신이 할당 할 때 dict2 = dict1, 당신의 사본을 제작하지 않습니다 dict1, 그것은 결과를 dict2위한 또 다른 이름 인 dict1.

사전과 같은 가변 유형을 복사하려면 모듈의 copy/ deepcopy사용 하십시오 copy.

import copy

dict2 = copy.deepcopy(dict1)

dict.copy()dict(dict1)복사본을 생성하는 동안 얕은 복사본 일뿐 입니다. 깊은 복사 를 원하면 copy.deepcopy(dict1)이 필요합니다. 예 :

>>> source = {'a': 1, 'b': {'m': 4, 'n': 5, 'o': 6}, 'c': 3}
>>> copy1 = x.copy()
>>> copy2 = dict(x)
>>> import copy
>>> copy3 = copy.deepcopy(x)
>>> source['a'] = 10  # a change to first-level properties won't affect copies
>>> source
{'a': 10, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> copy1
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> copy2
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> copy3
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> source['b']['m'] = 40  # a change to deep properties WILL affect shallow copies 'b.m' property
>>> source
{'a': 10, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}}
>>> copy1
{'a': 1, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}}
>>> copy2
{'a': 1, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}}
>>> copy3  # Deep copy's 'b.m' property is unaffected
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}

얕은 사본과 깊은 사본과 관련하여 Python copy모듈 문서에서 :

얕은 복사와 전체 복사의 차이는 복합 객체 (목록 또는 클래스 인스턴스와 같은 다른 객체를 포함하는 객체)에만 해당됩니다.

  • 단순 복사는 새로운 복합 객체를 생성 한 다음 가능한 한 원본에서 발견 된 객체에 대한 참조를 여기에 삽입합니다.
  • 전체 복사는 새로운 복합 개체를 생성 한 다음 재귀 적으로 원본에있는 개체의 복사본을 삽입합니다.

파이썬 3.5 이상에서는 ** unpackaging 연산자를 사용하여 얕은 사본을 얻는 더 쉬운 방법이 있습니다. Pep 448에 의해 정의되었습니다 .

>>>dict1 = {"key1": "value1", "key2": "value2"}
>>>dict2 = {**dict1}
>>>print(dict2)
{'key1': 'value1', 'key2': 'value2'}
>>>dict2["key2"] = "WHY?!"
>>>print(dict1)
{'key1': 'value1', 'key2': 'value2'}
>>>print(dict2)
{'key1': 'value1', 'key2': 'WHY?!'}

** 딕셔너리를 새 딕셔너리로 ​​압축 해제 한 다음 dict2에 할당합니다.

또한 각 사전에 고유 한 ID가 있는지 확인할 수도 있습니다.

>>>id(dict1)
 178192816

>>>id(dict2)
 178192600

깊은 복사가 필요한 경우 copy.deepcopy () 는 여전히 갈 길입니다.


Python 2.7과 3 모두 에서 dict 사본만드는 가장 쉽고 쉬운 방법 은 다음 같습니다.

단순 (단일 수준) 사전의 복사본을 만들려면 :

1. 사용 딕셔너리 () 메소드를 대신 기존 딕셔너리를 가리키는 레퍼런스를 생성하는 단계를 포함한다.

my_dict1 = dict()
my_dict1["message"] = "Hello Python"
print(my_dict1)  # {'message':'Hello Python'}

my_dict2 = dict(my_dict1)
print(my_dict2)  # {'message':'Hello Python'}

# Made changes in my_dict1 
my_dict1["name"] = "Emrit"
print(my_dict1)  # {'message':'Hello Python', 'name' : 'Emrit'}
print(my_dict2)  # {'message':'Hello Python'}

2. 파이썬 사전에 내장 된 update () 메소드 사용.

my_dict2 = dict()
my_dict2.update(my_dict1)
print(my_dict2)  # {'message':'Hello Python'}

# Made changes in my_dict1 
my_dict1["name"] = "Emrit"
print(my_dict1)  # {'message':'Hello Python', 'name' : 'Emrit'}
print(my_dict2)  # {'message':'Hello Python'}

중첩되거나 복잡한 사전의 복사본을 만들려면 :

일반 얕은 및 전체 복사 작업을 제공하는 기본 제공 복사 모듈을 사용합니다 . 이 모듈은 Python 2.7 및 3 * 모두에 있습니다.

import copy

my_dict2 = copy.deepcopy(my_dict1)

사전 이해력으로 새 사전을 만들 수도 있습니다. 이렇게하면 복사본을 가져 오지 않습니다.

dout = dict((k,v) for k,v in mydict.items())

물론 파이썬> = 2.7에서는 다음을 수행 할 수 있습니다.

dout = {k:v for k,v in mydict.items()}

그러나 이전 버전과의 호환성의 경우 최상위 방법이 더 좋습니다.


제공된 다른 솔루션 외에도 **사전을 빈 사전에 통합하는 데 사용할 수 있습니다 .

shallow_copy_of_other_dict = {**other_dict}.

이제 "얕은"복사본을 갖게됩니다 other_dict.

귀하의 예에 적용 :

>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = {**dict1}
>>> dict2
{'key1': 'value1', 'key2': 'value2'}
>>> dict2["key2"] = "WHY?!"
>>> dict1
{'key1': 'value1', 'key2': 'value2'}
>>>

포인터 : 얕은 사본과 깊은 사본의 차이점


Python의 할당 문은 개체를 복사하지 않으며 대상과 개체 사이에 바인딩을 만듭니다.

따라서 참조 하는 객체 dict2 = dict1사이에 또 ​​다른 바인딩이 발생 합니다.dict2dict1

dict를 복사하려면 copy module. 복사 모듈에는 두 가지 인터페이스가 있습니다.

copy.copy(x)
Return a shallow copy of x.

copy.deepcopy(x)
Return a deep copy of x.

얕은 복사와 전체 복사의 차이는 복합 객체 (목록 또는 클래스 인스턴스와 같은 다른 객체를 포함하는 객체)에만 해당됩니다.

얕은 사본 객체에 그것에 대한 참조가 원래 발견 삽입 (가능한 범위까지) 한 후 새로운 화합물 및 객체를 구성한다.

전체 복사 는 새로운 복합 개체를 생성 한 다음 재귀 적으로 원본에있는 개체의 복사본을 삽입합니다.

예를 들어 python 2.7.9에서 :

>>> import copy
>>> a = [1,2,3,4,['a', 'b']]
>>> b = a
>>> c = copy.copy(a)
>>> d = copy.deepcopy(a)
>>> a.append(5)
>>> a[4].append('c')

결과는 다음과 같습니다.

>>> a
[1, 2, 3, 4, ['a', 'b', 'c'], 5]
>>> b
[1, 2, 3, 4, ['a', 'b', 'c'], 5]
>>> c
[1, 2, 3, 4, ['a', 'b', 'c']]
>>> d
[1, 2, 3, 4, ['a', 'b']]

You can copy and edit the newly constructed copy in one go by calling the dict constructor with additional keyword arguments:

>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = dict(dict1, key2="WHY?!")
>>> dict1
{'key2': 'value2', 'key1': 'value1'}
>>> dict2
{'key2': 'WHY?!', 'key1': 'value1'}

This confused me too, initially, because I was coming from a C background.

In C, a variable is a location in memory with a defined type. Assigning to a variable copies the data into the variable's memory location.

But in Python, variables act more like pointers to objects. So assigning one variable to another doesn't make a copy, it just makes that variable name point to the same object.


Every variable in python (stuff like dict1 or str or __builtins__ is a pointer to some hidden platonic "object" inside the machine.

If you set dict1 = dict2,you just point dict1 to the same object (or memory location, or whatever analogy you like) as dict2. Now, the object referenced by dict1 is the same object referenced by dict2.

You can check: dict1 is dict2 should be True. Also, id(dict1) should be the same as id(dict2).

You want dict1 = copy(dict2), or dict1 = deepcopy(dict2).

The difference between copy and deepcopy? deepcopy will make sure that the elements of dict2 (did you point it at a list?) are also copies.

I don't use deepcopy much - it's usually poor practice to write code that needs it (in my opinion).


dict2 = dict1 does not copy the dictionary. It simply gives you the programmer a second way (dict2) to refer to the same dictionary.


dict1 is a symbol that references an underlying dictionary object. Assigning dict1 to dict2 merely assigns the same reference. Changing a key's value via the dict2 symbol changes the underlying object, which also affects dict1. This is confusing.

It is far easier to reason about immutable values than references, so make copies whenever possible:

person = {'name': 'Mary', 'age': 25}
one_year_later = {**person, 'age': 26}  # does not mutate person dict

This is syntactically the same as:

one_year_later = dict(person, age=26)

>>> dict2 = dict1
# dict2 is bind to the same Dict object which binds to dict1, so if you modify dict2, you will modify the dict1

There are many ways to copy Dict object, I simply use

dict_1 = {
           'a':1,
           'b':2
         }
dict_2 = {}
dict_2.update(dict_1)

Because python works with reference, so when you did dict2 = dict1 you pass a reference to dict2, that was the same as dict1. So, when you make a change in dict1 or dict2 you change a reference, and both dicts chages. Sorry if I mistake something on English.


As others have explained, the built-in dict does not do what you want. But in Python2 (and probably 3 too) you can easily create a ValueDict class that copies with = so you can be sure that the original will not change.

class ValueDict(dict):

    def __ilshift__(self, args):
        result = ValueDict(self)
        if isinstance(args, dict):
            dict.update(result, args)
        else:
            dict.__setitem__(result, *args)
        return result # Pythonic LVALUE modification

    def __irshift__(self, args):
        result = ValueDict(self)
        dict.__delitem__(result, args)
        return result # Pythonic LVALUE modification

    def __setitem__(self, k, v):
        raise AttributeError, \
            "Use \"value_dict<<='%s', ...\" instead of \"d[%s] = ...\"" % (k,k)

    def __delitem__(self, k):
        raise AttributeError, \
            "Use \"value_dict>>='%s'\" instead of \"del d[%s]" % (k,k)

    def update(self, d2):
        raise AttributeError, \
            "Use \"value_dict<<=dict2\" instead of \"value_dict.update(dict2)\""


# test
d = ValueDict()

d <<='apples', 5
d <<='pears', 8
print "d =", d

e = d
e <<='bananas', 1
print "e =", e
print "d =", d

d >>='pears'
print "d =", d
d <<={'blueberries': 2, 'watermelons': 315}
print "d =", d
print "e =", e
print "e['bananas'] =", e['bananas']


# result
d = {'apples': 5, 'pears': 8}
e = {'apples': 5, 'pears': 8, 'bananas': 1}
d = {'apples': 5, 'pears': 8}
d = {'apples': 5}
d = {'watermelons': 315, 'blueberries': 2, 'apples': 5}
e = {'apples': 5, 'pears': 8, 'bananas': 1}
e['bananas'] = 1

# e[0]=3
# would give:
# AttributeError: Use "value_dict<<='0', ..." instead of "d[0] = ..."

Please refer to the lvalue modification pattern discussed here: Python 2.7 - clean syntax for lvalue modification. The key observation is that str and int behave as values in Python (even though they're actually immutable objects under the hood). While you're observing that, please also observe that nothing is magically special about str or int. dict can be used in much the same ways, and I can think of many cases where ValueDict makes sense.


Nice explanations, I want to add the simplest rule you can refer to when thinking of Python variables which you assign equal with =. If the data type is immutable, you don't have to worry about the unexpected behavior you encountered. If the data type is mutable, you want to make sure you make a copy of it to prevent the unexpected behavior your encountered.

Immutable data types: string (a tuple of characters), tuple

Mutable data types: list, array, dictionary


because, dict2 = dict1, dict2 holds the reference to dict1. Both dict1 and dict2 points to the same location in the memory. This is just a normal case while working with mutable objects in python. When you are working with mutable objects in python you must be careful as it is hard to debug. Such as the following example.

 my_users = {
        'ids':[1,2],
        'blocked_ids':[5,6,7]
 }
 ids = my_users.get('ids')
 ids.extend(my_users.get('blocked_ids')) #all_ids
 print ids#output:[1, 2, 5, 6, 7]
 print my_users #output:{'blocked_ids': [5, 6, 7], 'ids': [1, 2, 5, 6, 7]}

This example intention is to get all the user ids including blocked ids. That we got from ids variable but we also updated the value of my_users unintentionally. when you extended the ids with blocked_ids my_users got updated because ids refer to my_users.


the following code, which is on dicts which follows json syntax more than 3 times faster than deepcopy

def CopyDict(dSrc):
    try:
        return json.loads(json.dumps(dSrc))
    except Exception as e:
        Logger.warning("Can't copy dict the preferred way:"+str(dSrc))
        return deepcopy(dSrc)

You can use directly:

dict2 = eval(repr(dict1))

where object dict2 is an independent copy of dict1, so you can modify dict2 without affecting dict1.

This works for any kind of object.

참고URL : https://stackoverflow.com/questions/2465921/how-to-copy-a-dictionary-and-only-edit-the-copy

반응형