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


80

이것은 파이썬을 배우는 두 번째 날입니다 (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. 이것이 올바른 설명입니까?



5
당신은 저장 42spam이제 저장, spam치즈 수단에 cheese = 42당신은 대체 그 이후 spam100, 당신은 편집하지 않은 cheese이유가 cheese아직 42.

15
Jonas가 그의 답변에서 지적했듯이 불변성은 여기에서 붉은 청어입니다. 목록과 같은 변경 가능한 객체를 사용하면 정확히 동일한 결과를 얻을 수 있습니다. 요점은 변수를 새 참조에 다시 할당 한다는 것 입니다.
다니엘 Roseman


27
@mini : 저는 파이썬 변수를 버킷보다는 레이블로 생각하는 것을 선호합니다. 당신 spam은 번호 42에 라벨 cheese을 붙 spam였습니다. 다음으로, 라벨 자체에 라벨을 붙 였습니다. 그럼 당신은 벗겨 spam그 오프 라벨과 번호 (100)에 넣어
팀 Pederick

답변:


81

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

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

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

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


2
같은 JS의 객체로
Dushyant 된 Bangal

7
불변성은 참조를 값인 것처럼 안전하게 처리 할 수 ​​있음을 의미하므로 중요합니다. 변경 가능한 객체를 값인 것처럼 취급하는 것은 더 위험합니다.
plugwash

21

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

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

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

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

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

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

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


20

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

첫째, 우리는 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

18

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

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

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

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

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

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


1
"pass-by-name"이라고 부르는 문제는 완전히 다른 의미를 가진 "call by name"이라는 매개 변수 전달 규칙이 이미 있다는 것입니다. 이름으로 호출에서 매개 변수 표현식은 함수가 매개 변수를 사용할 때마다 평가되며 함수가 매개 변수를 사용하지 않는 경우 평가되지 않습니다.
user2357112 모니카 지원

11

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


8

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


정수는 스택에있는 값 객체입니다.
Gert Kommer

예, 그들은 new Class()C ++에서 구문을 사용하여 만든 객체와 같습니다 . 게다가 파이썬에서는 무엇이든object 클래스 / 하위 클래스 의 인스턴스입니다 .
bakatrouble

4
CPython의 @GertKommer는 적어도 모든 개체가 힙에 있습니다. "가치 객체"는 구별되지 않습니다. 객체 만 있고 모든 것이 객체 입니다. 그렇기 때문에 일반적인 int의 크기는 전체 Py_Object 오버 헤드가 있기 때문에 Python 버전에 따라 약 28 바이트입니다. Small-int는 CPython 최적화로 캐시됩니다.
juanpa.arrivillaga jul.


1

Python에서 변수객체에 대한 참조 를 보유 합니다 . 개체 값과 보유 할당 된 메모리의 청크 인 헤더 . 개체의 헤더에는 가비지 컬렉션이 개체를 수집 할 수 있는지 여부를 식별 할 수 있도록 소스 코드에서이 개체가 참조되는 횟수를 나타내는 참조 카운터와 해당 유형이 포함됩니다.

이제 변수에 값을 할당 할 때 Python은 실제로 객체에 할당 된 메모리 위치에 대한 포인터 인 참조 를 할당합니다.

# x holds a reference to the memory location allocated for  
# the object(type=string, value="Hello World", refCounter=1)

x = "Hello World" 

이제 다른 유형의 개체를 동일한 변수에 할당 할 때 실제로 참조를 변경하여 다른 개체 (즉, 다른 메모리 위치)를 가리 킵니다. 변수에 다른 참조 (및 개체)를 할당 할 때 가비지 수집기는 소스 코드의 다른 변수가 참조하지 않는다고 가정하여 이전 개체에 할당 된 공간을 즉시 회수합니다.

# x holds a reference to the memory location allocated for  
# the object(type=string, value="Hello World", refCounter=1)

x = "Hello World" 

# Now x holds the reference to a different object(type=int, value=10, refCounter=1)
# and object(type=string, value="Hello World", refCounter=0) -which is not refereced elsewhere
# will now be garbage-collected.
x = 10

지금 당신의 모범을 보며,

spam object (type = int, value = 42, refCounter = 1)에 대한 참조를 보유합니다.

>>> spam = 42

이제 cheeseobject (type = int, value = 42, refCounter = 2)에 대한 참조도 보유합니다.

>>> cheese = spam

이제 스팸은 다른 객체 (type = int, value = 100, refCounter = 1)에 대한 참조를 보유합니다.

>>> spam = 100
>>> spam
100

하지만 치즈는 계속 object (type = int, value = 42, refCounter = 1)를 가리 킵니다.

>>> cheese
42

0

를 저장 spam = 42하면 메모리에 객체가 생성됩니다. 그럼 당신은 지정 cheese = spam이가 참조하는 객체 할당 spam에를 cheese. 마지막으로을 spam = 100변경하면 spam객체 만 변경됩니다 . 그래서 cheese = 42.


7
"그런 다음 cheese = spam을 할당하면 메모리에 다른 개체가 생성됩니다."아니요, 그렇지 않습니다. 그것은에 의해 참조되는 객체에 할당 spam하는 방법에 대해 cheese. 새 개체가 생성되지 않습니다.
juanpa.arrivillaga

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