[python] Python에서 한 변수의 값을 다른 변수에 할당하면 어떻게됩니까?

이것은 파이썬을 배우는 두 번째 날입니다 (C ++와 OOP의 기초를 알고 있습니다.). 파이썬의 변수에 대해 약간의 혼란이 있습니다.

현재 내가 이해하는 방법은 다음과 같습니다.

파이썬 변수는 객체 (변경 가능하거나 변경 불가능한)에 대한 참조 (또는 포인터?)입니다. 와 같은 것이 있으면 num = 5불변 객체 5가 메모리 어딘가에 생성되고 이름-객체 참조 쌍이 num특정 네임 스페이스에 생성됩니다. 가 있으면 a = num아무것도 복사되지 않지만 이제 두 변수가 동일한 객체를 참조 a하고 동일한 네임 스페이스에 추가됩니다.

이것이 제 책인 Automate the boring stuff with Python 에서 저를 혼란스럽게합니다. 초보자 용 책이므로 객체, 네임 스페이스 등을 언급하지 않고 다음 코드를 설명하려고합니다.

>>> spam = 42
>>> cheese = spam
>>> spam = 100
>>> spam
100
>>> cheese
42

그것이 제공하는 설명은 C ++ 책의 설명과 똑같습니다. 객체에 대한 참조 / 포인터를 다룰 때 만족스럽지 않습니다. 따라서이 경우 정수가 불변이므로 세 번째 줄에서 spam완전히 새로운 포인터 / 참조가 메모리의 다른 위치에 할당됩니다. 즉, 처음에 가리키는 메모리가 수정되지 않았습니다. 따라서에서 cheese참조하는 초기 객체를 참조했습니다 spam. 이것이 올바른 설명입니까?



답변

C ++ 개발자는 Python 변수를 포인터로 생각할 수 있습니다.

따라서를 작성할 때 spam = 100이것은 이전에 개체 42를 가리 키던 “포인터를 할당”하여 개체를 가리킴을 의미 합니다 100.

이전에, cheese같은 객체에 지점에 할당 spam할 일이있는 지적 42그 시간에. 수정하지 않았 cheese으므로 여전히을 가리 킵니다 42.

이 경우 불변성은 포인터 할당이 가리키는 개체에 대해 아무것도 변경하지 않기 때문에이 경우와 관련이 없습니다.


답변

내가 보는 방식은 언어에 대한 다른 견해입니다.

  • “언어 변호사”관점.
  • “실용적인 프로그래머”관점.
  • “구현 자”관점.

언어 변호사의 관점에서 파이썬 변수는 항상 객체를 “가리 킵니다”. 그러나 Java 및 C ++와 달리 == <=> = 등의 동작은 변수가 가리키는 개체의 런타임 유형에 따라 다릅니다. 또한 파이썬에서 메모리 관리는 언어에 의해 처리됩니다.

실용적인 프로그래머 관점에서 우리는 정수, 문자열, 튜플 등이 직선 값이 아니라 불변 * 객체라는 사실을 무관 한 세부 사항으로 취급 할 수 있습니다. 예외는 많은 양의 숫자 데이터를 저장할 때 작은 객체에 대한 참조로 가득 찬 배열로 끝나는 유형이 아닌 값을 직접 저장할 수있는 유형 (예 : numpy 배열)을 사용할 수 있다는 것입니다.

구현 자 관점에서 대부분의 언어는 지정된 동작이 올바른 경우 실제로 내부에서 작업이 수행되는 방식에 관계없이 구현이 정확하도록 일종의 as-if 규칙을 가지고 있습니다.

그렇습니다. 언어 변호사의 관점에서 귀하의 설명은 정확합니다. 당신의 책은 실용적인 프로그래머 관점에서 옳습니다. 구현이 실제로 수행하는 작업은 구현에 따라 다릅니다. cpython에서 정수는 새로 생성되지 않고 캐시 풀에서 작은 값의 정수를 가져 오지만 실제 객체입니다. 다른 구현 (예 : pypy 및 jython)이 무엇을하는지 잘 모르겠습니다.

* 여기에서 변경 가능한 객체와 변경 불가능한 객체의 차이점에 유의하십시오. 변경 가능한 객체를 사용하는 경우 다른 코드에서 변경 될 수 있으므로 “값처럼”취급하는 데주의해야합니다. 불변 객체를 사용하면 그러한 문제가 없습니다.


답변

포인터로 변수를 더 많거나 적을 수 있다는 것은 정확합니다. 그러나 예제 코드는 이것이 실제로 어떻게 작동 하는지 설명 하는 데 크게 도움이 될 것 입니다.

첫째, 우리는 id기능 을 많이 활용할 것입니다 .

객체의 “ID”를 반환합니다. 이것은 수명 동안이 객체에 대해 고유하고 상수가 보장되는 정수입니다. 수명이 겹치지 않는 두 개체는 동일한 id () 값을 가질 수 있습니다.

컴퓨터에서 다른 절대 값을 반환 할 가능성이 있습니다.

이 예를 고려하십시오.

>>> foo = 'a string'
>>> id(foo)
4565302640
>>> bar = 'a different string'
>>> id(bar)
4565321816
>>> bar = foo
>>> id(bar) == id(foo)
True
>>> id(bar)
4565302640

다음을 확인할 수 있습니다.

  • 원래 foo / bar는 다른 객체를 가리 키기 때문에 다른 ID를 갖습니다.
  • bar가 foo에 할당되면 이제 ID가 동일합니다. 이것은 둘 다 C ++ 포인터를 만들 때 볼 수있는 메모리의 동일한 위치를 가리키는 것과 비슷합니다.

foo의 값을 변경하면 다른 ID에 할당됩니다.

>>> foo = 42
>>> id(foo)
4561661488
>>> foo = 'oh no'
>>> id(foo)
4565257832

흥미로운 점은 정수가이 기능을 최대 256까지 암시 적으로 갖는다는 것입니다.

>>> a = 100
>>> b = 100
>>> c = 100
>>> id(a) == id(b) == id(c)
True

그러나 256 이상은 더 이상 사실이 아닙니다.

>>> a = 256
>>> b = 256
>>> id(a) == id(b)
True
>>> a = 257
>>> b = 257
>>> id(a) == id(b)
False

그러나 할당 ab전에 같이 참으로 ID를 동일하게 유지됩니다

>>> a = b
>>> id(a) == id(b)
True


답변

Python은 참조에 의한 전달 또는 값에 의한 전달이 아닙니다. 파이썬 변수는 포인터가 아니며 참조가 아니며 값이 아닙니다. 파이썬 변수는 이름 입니다.

동일한 구문 유형이 필요한 경우 “통과 별 별칭”으로 생각하거나 “개체 별 전달”로 생각하십시오. 변경 가능한 경우이를 나타내는 모든 변수에서 동일한 객체를 변경할 수 있지만 변수 (별칭)는 해당 변수 하나만 변경합니다.

도움이되는 경우 : C 변수는 값을 쓰는 상자입니다. Python 이름은 값에 입력하는 태그입니다.

Python 변수의 이름은 사실상 사전 인 전역 (또는 로컬) 네임 스페이스의 키입니다. 기본 값은 메모리의 일부 개체입니다. 할당은 해당 개체에 이름을 부여합니다. 한 변수를 다른 변수에 할당한다는 것은 두 변수가 동일한 개체의 이름임을 의미합니다. 한 변수를 다시 할당하면 다른 변수를 변경하지 않고 해당 변수에 의해 명명 된 개체가 변경됩니다. 태그를 이동했지만 이전 개체 나 그 위에있는 다른 태그는 변경하지 않았습니다.

CPython 구현의 기본 C 코드에서 모든 Python 객체는 PyObject*이므로 데이터에 대한 포인터 만있는 경우 (포인터에 대한 포인터, 직접 전달 된 값 없음) C처럼 작동한다고 생각할 수 있습니다.

Python은 값이 포인터 인 경우 값에 의한 전달이라고 말할 수 있습니다. 또는 Python은 참조가 복사 본인 참조에 의한 전달이라고 말할 수 있습니다.


답변

spam = 100파이썬 을 실행할 때 메모리에 하나 이상의 객체를 생성하지만 기존을 변경하지 마십시오. 따라서 여전히 cheese42와 spam100에 대한 포인터 가 있습니다.


답변

spam = 100줄 에서 일어나는 일은 이전 값 (유형의 객체에 대한 포인터)을 대체하는 것입니다.int 값이 42다른 객체에 대한 다른 포인터) (유형 int, 값 100)


답변

댓글에서 @DeepSpace가 언급했듯이 Ned Batchelder는 블로그에서 변수 (이름)와 값 할당을 잘 이해하고 있으며 Python 이름과 값에 대한 PyCon 2015, Facts and Myths 에서 강연을했습니다 . 모든 수준의 숙련도에서 Pythonistas에게 통찰력이 될 수 있습니다.