2016-09-23 1 views
0

파이썬 클래스를 초기화 할 때 이상한 효과가 있습니다. 확실한 것을 간과하고 있는지 확실하지 않습니다.값을 가져야 만하는 인스턴스 변수를 변경하여 파이썬 클래스 변수가 변경됨

0 [1, 1, 1] 
: 결과를 항복

class Test: 
    def __init__(self,x,y): 
    self.X = x 
    self.Y = y 
    self.X += 1 
    self.Y.append(1) 

x = 0 
y = [] 
Test(x,y) 
Test(x,y) 
Test(x,y) 
print x, y 

: 첫째

것부터, 나는 정수 값에 의해 전달하는 동안이 예와 같이 분명히 클래스에 전달 목록을 참조에 의해 전달되는 것을 알고 있어요

지금까지 너무 좋아. 이

stuff = {u'Item':['Test']} 
ds = {} 
DataSheet(stuff,ds) 
print ds 
DataSheet(stuff,ds) 
print ds 
DataSheet(stuff,ds) 
print ds 

수율처럼 호출

class DataSheet: 
    MISSINGKEYS = {u'Item': ["Missing"]} 

    def __init__(self,stuff,dataSheet): 
    self.dataSheet = dataSheet 
    if self.dataSheet.has_key(u'Item'): 
     self.dataSheet[u'Item'].append(stuff[u'Item']) 
    else: 
     self.dataSheet[u'Item'] = self.MISSINGKEYS[u'Item'] 

: 지금이 예를 살펴

stuff = {u'Item':['Test']} 
ds = {} 
DataSheet(stuff,ds) 
print DataSheet.MISSINGKEYS 
DataSheet(stuff,ds) 
print DataSheet.MISSINGKEYS 
DataSheet(stuff,ds) 
print DataSheet.MISSINGKEYS 

이 수율 :

{u'Item': ['Missing']} 
{u'Item': ['Missing', ['Test']]} 
{u'Item': ['Missing', ['Test'], ['Test']]} 

지금 대신 인쇄 MISSINGKEYS 수 있습니다

{u'Item': ['Missing']} 
{u'Item': ['Missing', ['Test']]} 
{u'Item': ['Missing', ['Test'], ['Test']]} 

똑같은 출력. 왜?

MISSINGKEYS은 클래스 변수이지만 의도적으로 변경되지 않습니다. 클래스가이 라인에 들어가는 첫 번째 통화에서

: 분명히 모든 것을 시작

self.dataSheet[u'Item'] = self.MISSINGKEYS[u'Item'] 

. 분명히 나는 ​​단지 self.dataSheet[u'Item']self.MISSINGKEYS[u'Item']의 값을 취하고 그 참조 또는 그와 같은 것이되지 않도록하고 싶다. 다음과 같은 두 가지 통화에서

self.dataSheet[u'Item'].append(stuff[u'Item']) 

대신 호출되는 라인과 self.dataSheet[u'Item']self.MISSINGKEYS[u'Item']되는 말아야에 append 작품.

이렇게하면 첫 번째 호출 후에 두 변수가 모두 동일한 개체를 참조하게됩니다. 여기에 무슨 일이 일어나고 있는지

ds == DataSheet.MISSINGKEYS 
Out[170]: True 
ds is DataSheet.MISSINGKEYS 
Out[171]: False 

누군가가 나에게 설명 할 수와 나는 그것을 피할 수있는 방법 :

그러나 동일하고 있지만, 그들은하지 않습니다?

편집

:

ds[u'Item'] is DataSheet.MISSINGKEYS[u'Item'] 
Out[172]: True 

그래서 좋아, 모두 사전 참조이 하나 개의 항목이 동일한 개체 : 나는이 시도. 대신 값을 할당 할 수 있습니까?여기

+0

'self.dataSheet [u'Item '] = self.MISSINGKEYS [u'Item']'는 당신이 그것을 바꿀 때 어디에서나 변경할 수 있도록 참조를 만든다. 'self.dataSheet [u'Item '] = list (self.MISSINGKEYS [u'Item']) 복사본을 만들어야합니다. –

+0

예. 이것은 내가 이해하지 못하는 파이썬에 관한 것입니다. 의식적으로 아무것도 복사하지 않아도되는 99.9 %의 모든 시간을 당신이 call-by-value를 수행하는 것처럼 작동시키고, 갑자기 이와 같은 것을 우연히 발견하게됩니다. . – Khris

+1

변경 가능한 객체를 사용하면 변경할 수있는 객체에 대한 참조를 만들고, 객체가 불변 인 경우에만 'i = 12; b = i; i + = 12'int는 불변이므로 여전히 12가된다. 그러나 변경 가능한 구조로 변경이 이루어 지므로 새로운 객체가 생성되지 않는다. 기본적으로 변경 가능한 값/obect를 사용하고 참조를 원할 필요가없는 경우 객체에 따라 복사하거나 deepcopy해야합니다. –

답변

1

:

else: 
    self.dataSheet[u'Item'] = self.MISSINGKEYS[u'Item'] 

당신은 MISSINGKEYS['Item']의 값 목록으로 dataShee['Item']을 설정하고 있습니다. 같은 목록. 시도

else: 
    self.dataSheet[u'Item'] = list(self.MISSINGKEYS[u'Item']) 

사본을 만들 수 있습니다.

+0

감사합니다. – Khris

1

파이썬 함수 호출에서 "pass by reference"와 "value by pass"의 관점에서 생각해 보면 일반적으로 유용하지 않습니다. 어떤 사람들은 "객체에 의한 전달"이라는 용어를 사용하기를 원합니다. Python의 모든 것은 객체이기 때문에 함수에 정수를 전달할 때 (C 언어의 경우) 실제로 정수 객체에 대한 포인터를 전달한다는 것을 기억하십시오. 첫 번째 코드 블록에서

당신은이 self.X에 바인딩 현재 정수 객체를 수정하지 않습니다이

self.X += 1 

을한다. 적절한 값을 가진 새로운 정수 개체를 만들고 해당 개체를 self.X 이름으로 바인딩합니다. 그 y 매개 변수로 Test.__init__에 전달 된 목록 개체로 발생 self.Y에 바인딩 된 현재 목록 개체를 돌연변이

self.Y.append(1) 

당신 있습니다와 반면

. 호출 코드의 y 목록 개체이므로 self.Y을 수정할 때 y 목록 개체가 호출 코드에서 변경됩니다. OTOH, 당신은

self.Y = ['new stuff'] 
다음 이름 self.Y이 새 목록에 바인딩 될

하고 (여전히 호출 코드에 y에 바인딩) 이전 목록과 같은 과제를 한 경우에 영향을받지 않을 것입니다.

이 문서가 도움이 될 수 있습니다., SO 베테랑 Ned Batchelder가 작성했습니다.