파이썬 변수 포인터입니까? 아니면 그들은 무엇입니까?


82

파이썬의 변수는 내가 아는 한 포인터 일뿐입니다.

이 규칙에 따라이 코드 조각의 결과를 가정 할 수 있습니다.

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].

내가 무엇을 잘못 이해하고 있습니까?



5
나는 표시되지 않습니다 i에 동일 봤는데한다3
Tooniis

@Tooniis j는 이제 i가 가리키는 것 이외의 다른 메모리 블록을 가리 킵니다. 당신은 아마 지금 쯤은 이해했지만, 단지의 경우 다른 사람이보고
Swaroop 조시

답변:


89

우리는 그것들을 참조라고 부릅니다. 그들은 이렇게 작동합니다

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

3
안녕하세요 John, 'Small int are interned'는 무슨 뜻입니까? 감사!
yuqli

6
@yuqli 파이썬에서는 숫자를 포함한 모든 것이 객체입니다. 작은 숫자 (-5,256)가 자주 사용되기 때문에 "인턴"되거나 CPython에 캐시됩니다. 따라서를 입력 할 때마다 40메모리에서 동일한 객체를 참조하게됩니다. 이 유형을 보려면 a, b = 256 및 test a is b. 이제 a, b = 257로 시도하십시오. 참조 : stackoverflow.com/a/1136852/3973834codementor.io/python/tutorial/...
에반 로시 카

3
제 경험상 파이썬 개발자들 사이에서 "이름" 이라고 부르는 것이 더 일반적입니다. "참조"라는 용어는 불필요한 C 수하물과 함께 제공되며 아마도 참조 카운팅을 사용하는 CPython ( 구현 )에 대해 Python ( 언어 )을 너무 많이 편향 시키고 있습니다 .
wim

33

변수는 포인터가 아닙니다. 변수에 할당 하면 이름을 개체에 바인딩 하는 것입니다. 그 이후부터는 이름이 리 바인드 될 때까지 이름을 사용하여 객체를 참조 할 수 있습니다.

첫 번째 예에서 이름 i은 값에 바인딩됩니다 5. 이름에 다른 값을 바인딩 j해도 영향을주지 않으므로 i나중에 인쇄 할 때 값의 i값은 그대로 유지 5됩니다.

두 번째 예에서는 모두 결합 i하고 j받는 동일한 목록 객체입니다. 목록의 내용을 수정하면 목록을 참조하는 데 사용하는 이름에 관계없이 변경 사항을 볼 수 있습니다.

"두 목록이 모두 변경되었습니다"라고 말하면 올바르지 않습니다. 목록은 하나 뿐이지 만이 를 참조하는 두 개의 이름 ( ij)이 있습니다.

관련 문서


15

파이썬 변수는 객체에 묶인 이름입니다

로부터 문서 :

이름은 개체를 나타냅니다. 이름은 이름 바인딩 작업에 의해 도입됩니다. 프로그램 텍스트 에서 이름 이 나타날 때마다 사용을 포함하는 가장 안쪽의 기능 블록에 설정된 이름의 바인딩을 나타냅니다 .

당신이 할 때

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]

지금 ij같은 목록을 모두 가리 킵니다. 그런 다음 귀하의 예제는 목록을 변경합니다.

i[0] = 5

파이썬 목록은 변경 가능한 객체이므로 한 참조에서 목록을 변경하고 다른 참조에서 볼 때 동일한 목록이므로 동일한 결과를 볼 수 있습니다.


9

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.ji[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 listPyObject*내부적 으로 포인터 의 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]두 이름이 가리키는 객체의 요소를 변경 했습니다.

기존 복합 객체의 값이 변경 될 수 있습니다.

이름을 통해 개체의 값을 변경할 때 이름은 변경되지 않습니다. 모두 ij여전히 값이 우리가 변경할 수있는 동일한 객체를 참조하십시오.

+- name i -+ -\
               \
                --> + <values> -+
               /    |  elements | --> [1, 2, 3]
+- name j -+ -/     +-----------+

중간 객체 는 가리키는 것을 직접 변경하고 여러 이름에서 참조 할 수 있다는 점에서 포인터와 유사하게 작동 합니다.


1
나는이 답변을 정말 좋아하지만 귀하의 예에서 ij할당 을 뒤집은 것 같습니다 . 당신은 시작 i = 5, j = 3다음 게시물의 나머지 부분을 반전. 즉, 이것은 OP의 질문에 대한 정의를 수행하고 실제로 어떤 일이 발생하는지 설명하는 유일한 대답입니다.
제레미는 래드클리프

1
@jeremyradcliff 머리를 올려 주셔서 감사합니다. 지금 수정해야합니다. 내가 더 놓친 경우 알려주세요.
MisterMiyagi

7

그것들은 포인터가 아니라 객체에 대한 참조입니다. 객체는 변경 가능하거나 변경 불가능할 수 있습니다. 변경 불가능한 객체는 수정 될 때 복사됩니다. 변경 가능한 객체는 제자리에서 변경됩니다. 정수는 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.

"불변 개체는 수정 될 때 복사됩니다." 그것은 약간 자기 모순입니다.
PM 2Ring

1

j=3레이블이 j더 이상 적용 (점)되지 않도록 설정 i하면 정수를 가리 키기 시작합니다 3. 이름 i은 여전히 ​​원래 설정 한 값인을 참조합니다 5.


1

'='기호의 왼쪽에있는 변수는 '='의 오른쪽에있는 값으로 할당됩니다.

i = 5

j = i --- j는 5

j = 3 --- j는 3 (값 5 덮어 쓰기)이지만 i와 관련하여 변경된 사항이 없습니다.

print(i)-그래서 이것은 5를 인쇄합니다


1

할당은 개체를 수정하지 않습니다. 변수가 가리키는 위치를 변경하는 것뿐입니다. 하나의 변수가 가리키는 위치를 변경해도 다른 변수가 가리키는 위치는 변경되지 않습니다.

목록과 사전이 변경 가능한 유형이라는 사실을 생각하고있을 것입니다. 실제 개체를 제자리에서 수정하는 연산자가 있으며 그중 하나를 사용하면 동일한 개체를 가리키는 모든 변수의 변경 사항을 볼 수 있습니다.

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을 값으로 볼 것입니다.

(실제 구현에서 숫자는 참조 유형이 아닐 수 있습니다. 변수가 실제로 값을 가리키는 것이 아니라 직접 값의 표현을 포함 할 가능성이 더 높습니다. 그러나 구현 세부 사항은 불변 유형과 관련된 의미 체계를 변경하지 않습니다. .)


1
그것은 가치 유형이 의미하는 것이 아닙니다. 값 유형은 마지막 단락에서 설명한 내용을 정확하게 의미하며 (값은 객체에 대한 참조 대신 전달 / 복사 됨) 내부적으로는 그렇지 않습니다 (CPython 및 PyPy에서는 JIT 컴파일러에서-모든 정수는 힙 할당 개체). 불변에 충실하십시오. 그것이 바로 거기에 필요한 단어입니다.

-1

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 ++에서는 참조 된 주소를 변경할 때 더 많은 유연성을 제공하는 메모리 객체 대신 메모리 포인터 (물론 포인터 구문을 사용하는 경우)를 반환합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.