파이썬의 변수는 내가 아는 한 포인터 일뿐입니다.
이 규칙에 따라이 코드 조각의 결과를 가정 할 수 있습니다.
i = 5
j = i
j = 3
print(i)
될 것 3
입니다. 하지만 예상치 못한 결과를 얻었습니다 5
.
또한 내 Python 책은이 예제를 다루고 있습니다.
i = [1,2,3]
j = i
i[0] = 5
print(j)
결과는입니다 [5,2,3]
.
내가 무엇을 잘못 이해하고 있습니까?
파이썬의 변수는 내가 아는 한 포인터 일뿐입니다.
이 규칙에 따라이 코드 조각의 결과를 가정 할 수 있습니다.
i = 5
j = i
j = 3
print(i)
될 것 3
입니다. 하지만 예상치 못한 결과를 얻었습니다 5
.
또한 내 Python 책은이 예제를 다루고 있습니다.
i = [1,2,3]
j = i
i[0] = 5
print(j)
결과는입니다 [5,2,3]
.
내가 무엇을 잘못 이해하고 있습니까?
i
에 동일 봤는데한다3
답변:
우리는 그것들을 참조라고 부릅니다. 그들은 이렇게 작동합니다
i = 5 # create int(5) instance, bind it to i
j = i # bind j to the same int as i
j = 3 # create int(3) instance, bind it to j
print i # i still bound to the int(5), j bound to the int(3)
작은 정수는 인턴이지만이 설명에서는 중요하지 않습니다.
i = [1,2,3] # create the list instance, and bind it to i
j = i # bind j to the same list as i
i[0] = 5 # change the first item of i
print j # j is still bound to the same list as i
40
메모리에서 동일한 객체를 참조하게됩니다. 이 유형을 보려면 a, b = 256 및 test a is b
. 이제 a, b = 257로 시도하십시오. 참조 : stackoverflow.com/a/1136852/3973834 및 codementor.io/python/tutorial/...
변수는 포인터가 아닙니다. 변수에 할당 하면 이름을 개체에 바인딩 하는 것입니다. 그 이후부터는 이름이 리 바인드 될 때까지 이름을 사용하여 객체를 참조 할 수 있습니다.
첫 번째 예에서 이름 i
은 값에 바인딩됩니다 5
. 이름에 다른 값을 바인딩 j
해도 영향을주지 않으므로 i
나중에 인쇄 할 때 값의 i
값은 그대로 유지 5
됩니다.
두 번째 예에서는 모두 결합 i
하고 j
받는 동일한 목록 객체입니다. 목록의 내용을 수정하면 목록을 참조하는 데 사용하는 이름에 관계없이 변경 사항을 볼 수 있습니다.
"두 목록이 모두 변경되었습니다"라고 말하면 올바르지 않습니다. 목록은 하나 뿐이지 만이 를 참조하는 두 개의 이름 ( i
및 j
)이 있습니다.
관련 문서
로부터 문서 :
이름은 개체를 나타냅니다. 이름은 이름 바인딩 작업에 의해 도입됩니다. 프로그램 텍스트 에서 이름 이 나타날 때마다 사용을 포함하는 가장 안쪽의 기능 블록에 설정된 이름의 바인딩을 나타냅니다 .
당신이 할 때
i = 5
j = i
다음과 같이하는 것과 같습니다.
i = 5
j = 5
j
을 가리 키지 않고 i
할당 후에 j
그것이 i
존재 하는지 모릅니다 . 할당 당시에 가리키는 j
모든 i
것에 묶여 있습니다.
같은 줄에 할당을했다면 다음과 같습니다.
i = j = 5
그리고 결과는 똑같을 것입니다.
따라서 나중에
i = 3
j
가리키는 내용을 변경하지 않고 바꿀 수 있습니다 . 가리키는 내용을 j = 3
변경하지 않습니다 i
.
따라서 이렇게하면 :
i = [1,2,3]
j = i
이 작업을 수행하는 것과 동일합니다.
i = j = [1,2,3]
지금 i
과 j
같은 목록을 모두 가리 킵니다. 그런 다음 귀하의 예제는 목록을 변경합니다.
i[0] = 5
파이썬 목록은 변경 가능한 객체이므로 한 참조에서 목록을 변경하고 다른 참조에서 볼 때 동일한 목록이므로 동일한 결과를 볼 수 있습니다.
TLDR : Python 이름 은 자동 역 / 참조가있는 포인터처럼 작동하지만 명시 적 포인터 작업을 허용하지 않습니다. 다른 대상은 포인터와 유사하게 작동하는 간접적을 나타냅니다.
CPython 구현은 내부적 으로 유형의 포인터를PyObject*
사용합니다 . 따라서 이름 의미를 포인터 작업으로 변환 할 수 있습니다. 핵심은 실제 개체 와 이름 을 구분하는 것 입니다.
예제 Python 코드에는 이름 ( i
)과 객체 ( 5
)가 모두 포함됩니다 .
i = 5 # name `i` refers to object `5`
j = i # ???
j = 3 # name `j` refers to object `3`
이것은 대략 별도의 이름과 객체를 가진 C 코드로 변환 될 수 있습니다 .
int three=3, five=5; // objects
int *i, *j; // names
i = &five; // name `i` refers to position of object `5`
j = i; // name `j` refers to referent of `i`
j = &three; // name `j` refers to position of object `3`
중요한 부분은 "포인터로서의 이름"은 객체를 저장하지 않는다는 것입니다! 우리는 정의하지 *i = five
않았지만 i = &five
. 이름과 개체는 서로 독립적으로 존재합니다.
이름 은 메모리의 기존 개체 만 가리 킵니다 .
이름에서 이름으로 할당 할 때 개체가 교환되지 않습니다! 우리가 정의 할 때 j = i
이것은 j = &five
. 둘 다 다른 것과 연결되어 있지 i
않습니다 j
.
+- name i -+ -\
\
--> + <five> -+
/ | 5 |
+- name j -+ -/ +----------+
따라서 한 이름의 대상을 변경해도 다른 이름에는 영향을주지 않습니다 . 특정 이름이 가리키는 것만 업데이트합니다.
파이썬에는 또한 속성 참조 ( ), 구독 ( ) 및 슬라이싱 ( ) 과 같은 다른 종류의 이름과 유사한 요소가 있습니다. 개체를 직접 참조하는 이름과 달리 세 가지 모두 개체의 요소를 간접적으로 참조 합니다.i.j
i[j]
i[:j]
예제 코드에는 이름 ( i
)과 구독 ( i[0]
)이 모두 포함됩니다 .
i = [1,2,3] # name `i` refers to object `[1, 2, 3]`
j = i # name `j` refers to referent of `i`
i[0] = 5 # ???
CPython list
은 PyObject*
내부적 으로 포인터 의 C 배열을 사용합니다 . 이것은 다시 대략적인 이름과 객체를 가진 C 코드로 변환 될 수 있습니다.
typedef struct{
int *elements[3];
} list; // length 3 `list` type
int one = 1, two = 2, three = 3, five = 5;
list values = {&one, &two, &three}; // objects
list *i, *j; // names
i = &values; // name `i` refers to object `[1, 2, 3]`
j = i; // name `j` refers to referent of `i`
i->elements[0] = &five; // leading element of `i` refers to object `5`
중요한 부분은 우리가 이름을 변경하지 않았다는 것입니다! 우리는 i->elements[0]
두 이름이 가리키는 객체의 요소를 변경 했습니다.
기존 복합 객체의 값이 변경 될 수 있습니다.
이름을 통해 개체의 값을 변경할 때 이름은 변경되지 않습니다. 모두 i
와 j
여전히 값이 우리가 변경할 수있는 동일한 객체를 참조하십시오.
+- name i -+ -\
\
--> + <values> -+
/ | elements | --> [1, 2, 3]
+- name j -+ -/ +-----------+
중간 객체 는 가리키는 것을 직접 변경하고 여러 이름에서 참조 할 수 있다는 점에서 포인터와 유사하게 작동 합니다.
i
및 j
할당 을 뒤집은 것 같습니다 . 당신은 시작 i = 5
, j = 3
다음 게시물의 나머지 부분을 반전. 즉, 이것은 OP의 질문에 대한 정의를 수행하고 실제로 어떤 일이 발생하는지 설명하는 유일한 대답입니다.
그것들은 포인터가 아니라 객체에 대한 참조입니다. 객체는 변경 가능하거나 변경 불가능할 수 있습니다. 변경 불가능한 객체는 수정 될 때 복사됩니다. 변경 가능한 객체는 제자리에서 변경됩니다. 정수는 i 및 j 변수로 참조하는 변경 불가능한 객체입니다. 목록은 변경 가능한 객체입니다.
첫 번째 예에서
i=5
# The label i now references 5
j=i
# The label j now references what i references
j=3
# The label j now references 3
print i
# i still references 5
두 번째 예에서 :
i=[1,2,3]
# i references a list object (a mutable object)
j=i
# j now references the same object as i (they reference the same mutable object)
i[0]=5
# sets first element of references object to 5
print j
# prints the list object that j references. It's the same one as i.
'='기호의 왼쪽에있는 변수는 '='의 오른쪽에있는 값으로 할당됩니다.
i = 5
j = i
--- j는 5
j = 3
--- j는 3 (값 5 덮어 쓰기)이지만 i와 관련하여 변경된 사항이 없습니다.
print(i)
-그래서 이것은 5를 인쇄합니다
할당은 개체를 수정하지 않습니다. 변수가 가리키는 위치를 변경하는 것뿐입니다. 하나의 변수가 가리키는 위치를 변경해도 다른 변수가 가리키는 위치는 변경되지 않습니다.
목록과 사전이 변경 가능한 유형이라는 사실을 생각하고있을 것입니다. 실제 개체를 제자리에서 수정하는 연산자가 있으며 그중 하나를 사용하면 동일한 개체를 가리키는 모든 변수의 변경 사항을 볼 수 있습니다.
x = []
y = x
x.append(1)
# x and y both are now [1]
그러나 할당은 여전히 포인터를 이동합니다.
x = [2]
# x now points to new list [2]; y still points to old list [1]
사전 및 목록과 달리 숫자는 변경할 수 없습니다. 당신이한다면 x = 3; x += 2
, 당신은 숫자 3을 숫자 5로 바꾸는 것이 아닙니다. x
대신 변수 를 5로 지정합니다. 3은 여전히 변경되지 않았으며이를 가리키는 모든 변수는 여전히 3을 값으로 볼 것입니다.
(실제 구현에서 숫자는 참조 유형이 아닐 수 있습니다. 변수가 실제로 값을 가리키는 것이 아니라 직접 값의 표현을 포함 할 가능성이 더 높습니다. 그러나 구현 세부 사항은 불변 유형과 관련된 의미 체계를 변경하지 않습니다. .)
Python에서는 반환되는 메모리 조각 자체를 포함하여 모든 것이 객체입니다. 즉, 새 메모리 청크가 생성 될 때 (생성 한 항목 : int, str, 사용자 정의 객체 등)에 관계없이 새 메모리 객체를 갖게됩니다. 귀하의 경우 이것은 새로운 (메모리) 객체를 생성하고 따라서 새로운 주소를 갖는 3에 대한 할당입니다.
다음을 실행하면 내가 의미하는 바를 쉽게 알 수 있습니다.
i = 5
j = i
print("id of j: {}", id(j))
j = 3
print("id of j: {}", id(j))
IMO, 메모리 측면에서 이것은 C와 Python의 주요 이해 / 차이입니다. C / C ++에서는 참조 된 주소를 변경할 때 더 많은 유연성을 제공하는 메모리 객체 대신 메모리 포인터 (물론 포인터 구문을 사용하는 경우)를 반환합니다.