+=가 목록에서 예기치 않게 작동하는 이유는 무엇입니까?
그+=
python의 연산자가 목록에서 예기치 않게 작동하는 것 같습니다.여기서 무슨 일이 일어나고 있는지 누가 말해줄 수 있나요?
class foo:
bar = []
def __init__(self,x):
self.bar += [x]
class foo2:
bar = []
def __init__(self,x):
self.bar = self.bar + [x]
f = foo(1)
g = foo(2)
print f.bar
print g.bar
f.bar += [3]
print f.bar
print g.bar
f.bar = f.bar + [4]
print f.bar
print g.bar
f = foo2(1)
g = foo2(2)
print f.bar
print g.bar
산출량
[1, 2]
[1, 2]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3]
[1]
[2]
foo += bar
을 미치는 반면에, 의모든인영에미향스치반것보면이처는럼는수을스업턴반,foo = foo + bar
제가 기대하는 방식으로 행동하는 것 같습니다.
그+=
연산자를 "수직 할당 연산자"라고 합니다.
일반적인 대답은+=
호출을 시도합니다.__iadd__
특별한 방법, 그리고 만약 그것이 불가능하다면 그것은 사용하려고 합니다.__add__
대신. 이 사이의 입니다.그래서 문제는 이 특별한 방법들 사이의 차이입니다.
그__iadd__
특별한 방법은 임플레이스 추가를 위한 것입니다. 즉, 그것은 그것이 작용하는 객체를 변형시킵니다. 그__add__
특수 메소드는 새 개체를 반환하며 표준에도 사용됩니다.+
교환입니다.
래서그 ▁the 때.+=
는 는다가객사용다됩니체에진연음산을이 있는 됩니다.__iadd__
개체가 제자리에서 수정되었다고 정의했습니다.그렇지 않으면 플레인을 대신 사용하려고 합니다.__add__
새 개체를 반환합니다.
그렇기 때문에 목록과 같은 가변 유형에 대해+=
튜플,, 의 경우 새 됩니다(" 의체 값변반면을경는객하다문자열니대됩반경튜유객새환같불형정신변우의의체로가은수운와플▁changes▁is,객▁object다▁like▁instead의값체반대됩▁returned니환정'신▁the▁a▁whereas▁(▁fora += b
와 동등해집니다.a = a + b
).
▁both를 모두 지원하는 __iadd__
그리고.__add__
따라서 어떤 것을 사용하는지 주의해야 합니다. a += b
부를 것입니다__iadd__
돌연변이 돌변이연.a
에, 면에반에.a = a + b
새 되어 이 를 새개를 에할다니합당여에 합니다.a
이들은 동일한 작업이 아닙니다!
>>> a1 = a2 = [1, 2]
>>> b1 = b2 = [1, 2]
>>> a1 += [3] # Uses __iadd__, modifies a1 in-place
>>> b1 = b1 + [3] # Uses __add__, creates new list, assigns it to b1
>>> a2
[1, 2, 3] # a1 and a2 are still the same list
>>> b2
[1, 2] # whereas only b1 was changed
가 불사유형경없(우가는자변용사경)(우▁you▁ 경우).__iadd__
)a += b
그리고.a = a + b
동등합니다.이것이 바로 당신이 사용할 수 있는 것입니다.+=
유형에 결정으로 수 . 그렇지 수 을 고려하기 까지는.+=
숫자와 같은 불변의 유형에서!
일반적인 경우 스콧 그리피스의 답변을 참조하십시오.하지만 당신과 같은 목록을 다룰 때,+=
는 연자는의약다니입자산다의 입니다.someListObject.extend(iterableObject)
확장()의 설명서를 참조하십시오.
그extend
함수는 매개 변수의 모든 요소를 목록에 추가합니다.
를 할 때.foo += something
.foo
제자리에서, 따라서 당신은 그 이름의 참조를 변경하지 않습니다.foo
를 가리키지만 목록 개체를 직접 변경하는 것입니다.와 함께foo = foo + something
당신은 실제로 새로운 목록을 만들고 있습니다.
이 예제 코드는 다음을 설명합니다.
>>> l = []
>>> id(l)
13043192
>>> l += [3]
>>> id(l)
13043192
>>> l = l + [3]
>>> id(l)
13059216
새 목록을 다음으로 재할당할 때 참조가 어떻게 변경되는지 확인합니다.l
.
~하듯이bar
인스턴스 변수 대신 클래스 변수입니다. 제자리에서 수정하면 해당 클래스의 모든 인스턴스에 영향을 줍니다.하지만 다시 정의할 때self.bar
에는 별도의 변수 " " " " " " " " 이 있습니다.self.bar
다른 클래스 인스턴스에 영향을 미치지 않습니다.
여서문제는기,,bar
인스턴스 변수가 아닌 클래스 특성으로 정의됩니다.
foo
은 클스속에수다니에서 됩니다.init
모든 인스턴스가 영향을 받는 이유입니다.
foo2
속성을 되며, 한 " " " " " " (") " " " " " " " " 를 얻습니다.bar
.
올바른 구현은 다음과 같습니다.
class foo:
def __init__(self, x):
self.bar = [x]
물론, 클래스 속성은 완전히 합법적입니다.실제로 다음과 같은 클래스의 인스턴스를 만들지 않고 액세스하고 수정할 수 있습니다.
class foo:
bar = []
foo.bar = [x]
여기에는 두 가지가 관련되어 있습니다.
1. class attributes and instance attributes
2. difference between the operators + and += for lists
+
은 " 교이호다니합출환원▁the다▁operator▁calls니"라고 부릅니다.__add__
.피연산자에서 모든 요소를 가져와서 순서를 유지하는 요소를 포함하는 새 목록을 만듭니다.
+=
호출 환화전교__iadd__
리스트에 있는 방법.반복 가능한 항목을 사용하고 반복 가능한 항목의 모든 요소를 제자리에 목록에 추가합니다.새 목록 개체를 만들지 않습니다.
에.foo
self.bar += [x]
할당문은 아니지만 실제로는 로 번역됩니다.
self.bar.__iadd__([x]) # modifies the class attribute
에서 수정하고 합니다.extend
.
에.foo2
대로배, 는술진서정에 .init
방법
self.bar = self.bar + [x]
다음과 같이 분해할 수 있습니다.
" 인턴스특없습다니"이 없습니다.bar
이름의 클래스 속성에 .bar
추하여새만목다듭니록을가ending를 추가하여 새 .x
문장은 다음과같이 됩니다.
self.bar = self.bar.__add__([x]) # bar on the lhs is the class attribute
속성 " 그다인스속만듭다니성을스턴런bar
새로 만든 목록을 할당합니다.:bar
가 과의는 RH다와다니릅과 .bar
왼쪽에
클래스 클스인의경우스의 .foo
,bar
인스턴스 속성이 아닌 클래스 속성입니다. 속성 ▁to▁▁change에 대한 모든 변경 사항bar
모든 인스턴스에 반영됩니다.
의 각 foo2
속성 고한인스특있습다니가 .bar
속성과 다릅니다.bar
.
f = foo2(4)
print f.bar # accessing the instance attribute. prints [4]
print f.__class__.bar # accessing the class attribute. prints []
이것으로 모든 것이 해결되기를 바랍니다.
많은 시간이 지나고 많은 옳은 말들이 나왔지만, 두 가지 효과를 묶은 답은 없습니다.
두 가지 효과가 있습니다.
- "특별한", 어쩌면 눈에 띄지 않는 목록의 행동.
+=
(Scott Griffiths가 언급한 바와 같이) - 인스턴스 속성뿐만 아니라 클래스 속성이 관련되어 있다는 사실(캔 Berk Büder에 의해 언급됨)
에.foo
,__init__
메서드가 클래스 속성을 수정합니다.은 이는유 때문입니다.self.bar += [x]
에해는하로 됩니다.self.bar = self.bar.__iadd__([x])
.__iadd__()
는 인플레이스 수정을 위한 것이므로 목록을 수정하고 참조를 반환합니다.
인스턴스 dict가 수정되었지만 클래스 dict에 이미 동일한 할당이 포함되어 있기 때문에 일반적으로 이것은 필요하지 않습니다.그래서 이 세부 사항은 거의 눈에 띄지 않습니다 - 당신이 하는 경우를 제외하고.foo.bar = []
사후에예를 들어 보겠습니다.bar
위와 같은 사실 덕분에 그대로 유지됩니다.
에.foo2
그 의 하만의, 학급지.bar
사용되지만 터치되지는 않습니다. 신에대, a.[x]
개체를 합니다.self.bar.__add__([x])
개체를 수정하지 않는 여기서 호출됩니다.클래스의 속성은 수정된 상태로 유지되는 동안 결과가 인스턴스 dict에 저장되고 인스턴스에 새 목록이 dict로 제공됩니다.
의 ... = ... + ...
그리고.... += ...
이후 할당에도 영향을 미칩니다.
f = foo(1) # adds 1 to the class's bar and assigns f.bar to this as well.
g = foo(2) # adds 2 to the class's bar and assigns g.bar to this as well.
# Here, foo.bar, f.bar and g.bar refer to the same object.
print f.bar # [1, 2]
print g.bar # [1, 2]
f.bar += [3] # adds 3 to this object
print f.bar # As these still refer to the same object,
print g.bar # the output is the same.
f.bar = f.bar + [4] # Construct a new list with the values of the old ones, 4 appended.
print f.bar # Print the new one
print g.bar # Print the old one.
f = foo2(1) # Here a new list is created on every call.
g = foo2(2)
print f.bar # So these all obly have one element.
print g.bar
를 를 여 개 체 ID 있 수 니 다 습 확 할 인 사 용 하 의 ▁with ▁objects ▁of ▁the ▁identity ▁you ▁verify 다 ▁the 를 니 있 ▁can 습 를 수용 사print id(foo), id(f), id(g)
(추가사항잊마십오시지을마오추시십▁()를 잊지 마세요.()
(Python3)에 있는 경우).
BTW: 더+=
연산자를 "임의 할당"이라고 하며 일반적으로 가능한 한 장소에서 수정을 수행하기 위한 것입니다.
추가 과제 PEP 203을 인용하고 참조할 가치가 있는 것처럼 보이지만, 다른 답변은 거의 다 포함된 것처럼 보일 것입니다.
그들[증강된 할당 연산자]은 왼쪽 측면 객체가 지원할 때 연산이 '인플레이스'로 수행되고 왼쪽 측면은 한 번만 평가된다는 점을 제외하고는 일반 이진 형식과 동일한 연산자를 구현합니다.
...
파이썬에서 증강된 할당의 이면에 있는 아이디어는 이진 연산의 결과를 왼쪽 피연산자에 저장하는 일반적인 관행을 쓰는 것이 더 쉬울 뿐만 아니라 해당 왼쪽 피연산자가 수정된 복사본을 만드는 것이 아니라 '자체적으로' 작동해야 한다는 것을 아는 방법이라는 것입니다.
>>> elements=[[1],[2],[3]]
>>> subset=[]
>>> subset+=elements[0:1]
>>> subset
[[1]]
>>> elements
[[1], [2], [3]]
>>> subset[0][0]='change'
>>> elements
[['change'], [2], [3]]
>>> a=[1,2,3,4]
>>> b=a
>>> a+=[5]
>>> a,b
([1, 2, 3, 4, 5], [1, 2, 3, 4, 5])
>>> a=[1,2,3,4]
>>> b=a
>>> a=a+[5]
>>> a,b
([1, 2, 3, 4, 5], [1, 2, 3, 4])
>>> a = 89
>>> id(a)
4434330504
>>> a = 89 + 1
>>> print(a)
90
>>> id(a)
4430689552 # this is different from before!
>>> test = [1, 2, 3]
>>> id(test)
48638344L
>>> test2 = test
>>> id(test)
48638344L
>>> test2 += [4]
>>> id(test)
48638344L
>>> print(test, test2) # [1, 2, 3, 4] [1, 2, 3, 4]```
([1, 2, 3, 4], [1, 2, 3, 4])
>>> id(test2)
48638344L # ID is different here
우리는 불변 객체(이 경우 정수)를 수정하려고 하면 Python이 대신 다른 객체를 제공한다는 것을 알 수 있습니다.반면에 우리는 변경 가능한 개체(목록)를 변경할 수 있으며 전체적으로 동일한 개체로 유지할 수 있습니다.
ref : https://medium.com/ @tyastropheus/cisco-module-i-memory-management for muttable-module-module-21507d1e5b95
또한 아래 URL을 참조하여 얕은 복사와 딥 복사를 이해하십시오.
https://www.geeksforgeeks.org/copy-python-deep-copy-shallow-copy/
listname.extend()
에 잘 합니다 :) :)
언급URL : https://stackoverflow.com/questions/2347265/why-does-behave-unexpectedly-on-lists
'source' 카테고리의 다른 글
mariadb 기억이 안정되는 데 얼마나 걸립니까? (0) | 2023.07.10 |
---|---|
Oracle 데이터베이스의 모든 저장 프로시저를 나열하려면 어떤 SQL을 사용해야 합니까? (0) | 2023.07.10 |
Wi-Fi를 통해 Android 응용 프로그램을 실행/설치/디버깅하시겠습니까? (0) | 2023.07.10 |
spring-boot-starter-web의 기본 JSON 오류 응답은 어디에서 왔으며 어떻게 조정합니까? (0) | 2023.07.10 |
오라클이 제약 조건을 삭제/재작성하지 않고 캐스케이드 삭제를 수행하도록 강제하는 방법 (0) | 2023.07.10 |