본문 바로가기
C.W.K.
Stream
Lesson 04 of 07 · published

변수는 상자가 아니라 이름표야

~22 min · 변수, 할당, 정체성, 동등성, mutability, 참조, id, 튜플-언패킹

Level 0호기심
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

할당은 객체에 이름을 붙이는 일

a = value는 값을 상자에 복사해 넣는 동작이 아니야. 이미 만들어진 객체를 a라는 이름이 가리키게 해. 그래서 b = a 뒤에는 두 이름이 같은 객체를 가리킬 수 있어.

같은 값과 같은 객체는 다르다

==는 값이 같은지 묻고, is는 바로 그 객체인지 물어. None처럼 하나뿐인 객체는 is None으로 비교하지만, 문자열이나 숫자의 값 비교에 is를 쓰면 안 돼. id()는 실행 중 객체의 정체성을 확인하는 데 도움이 돼.

가변 객체의 별명이 만드는 놀라움

리스트를 두 이름이 함께 가리킨 상태에서 한쪽을 바꾸면 다른 쪽에서도 바뀐 값이 보여. 별도의 바깥 컨테이너가 필요하면 얕은 복사를, 안쪽의 가변 객체까지 분리해야 하면 deepcopy를 검토해. 무엇을 공유하고 무엇을 복제할지 먼저 정하는 게 핵심이야.

언패킹과 증강 할당

a, b = b, a처럼 여러 값을 한 번에 풀어 받을 수 있어. +=는 정수처럼 불변인 객체에서는 새 값을 다시 묶지만, 리스트에서는 같은 객체를 제자리에서 바꿀 수 있어. 같은 문법이 객체의 가변성에 따라 다른 정체성 결과를 낳아.

미리 보는 함정: 함수 기본값에 리스트를 두면 호출 사이에도 같은 객체가 남아. 함수 수업에서 안전한 형태를 다시 배울 거야.

Code

이름은 객체를 가리킨다·python
>>> x = 42
>>> y = x        # y 가 이제 x 와 *같은* 객체를 가리킴
>>> id(x) == id(y)
True
>>> x = 100      # x 가 새 객체로 다시 가리킴; y 는 여전히 42 가리킴
>>> y
42
>>> id(x) == id(y)
False
값의 같음과 객체의 같음·python
>>> a = [1, 2, 3]
>>> b = [1, 2, 3]    # 같은 내용의 *다른* list
>>> a == b
True               # 값이 같음
>>> a is b
False              # 메모리상 다른 객체

>>> c = a            # c 가 a 와 *같은* 객체를 가리킴
>>> a is c
True               # 같은 객체

>>> # None 체크의 PEP 8 idiom:
>>> x = None
>>> if x is None: ...    # 맞음
>>> if x == None: ...    # 동작은 하지만 idiomatic 아님
같은 가변 객체를 가리킬 때 생기는 일·python
>>> a = [1, 2, 3]
>>> b = a            # b 가 copy 받는 게 아님 — *같은* list 받음
>>> b.append(4)      # b 통해 변경
>>> a                # a 도 보임 — 처음부터 list 는 하나뿐
[1, 2, 3, 4]

>>> # 진짜 copy:
>>> b = a.copy()     # shallow copy — 새 list, 같은 element
>>> # 또는 b = list(a) 또는 b = a[:]

>>> # 중첩 구조 (list of lists) 면 deep copy 필요:
>>> import copy
>>> b = copy.deepcopy(a)
튜플 언패킹으로 값 바꾸기·python
>>> # Pythonic swap — 임시 변수가 필요 없어.
>>> a, b = 1, 2
>>> a, b = b, a
>>> a, b
(2, 1)

>>> # 함수에서 언패킹:
>>> def get_pair():
...     return "hello", 42
>>> name, count = get_pair()

>>> # Star 언패킹 — 나머지가 list 로:
>>> first, *middle, last = [1, 2, 3, 4, 5]
>>> first, middle, last
(1, [2, 3, 4], 5)
정수와 리스트에서 +=가 다른 이유·python
>>> # Immutable: x += 1 은 새 int 생성.
>>> x = 10
>>> id(x)
4327683600
>>> x += 1
>>> id(x)
4327683632       # 다른 객체

>>> # Mutable: list += other 는 in-place 변경.
>>> a = [1, 2, 3]
>>> id(a)
4385720128
>>> a += [4, 5]
>>> id(a)
4385720128       # 같은 객체 — a 가 in-place 변경됨

>>> # 비교: a = a + [4, 5] 는 새 list 만들고 a 를 다시 가리키게 했을 거야.
>>> # 비슷해 보여, 미묘하게 달라.
가변 기본값 함정 미리 보기·python
# 이렇게 하지 마:
def add_item(item, items=[]):
    items.append(item)
    return items

print(add_item('a'))    # ['a']
print(add_item('b'))    # ['a', 'b']  — 깜짝!
print(add_item('c'))    # ['a', 'b', 'c']  — 셋이 한 list 공유

# 이렇게 해:
def add_item(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

print(add_item('a'))    # ['a']
print(add_item('b'))    # ['b']  — 호출마다 fresh list

External links

Exercise

REPL에서 a = [1, 2, 3], b = a를 만든 뒤 b.append(4)가 a에 어떤 영향을 줄지 먼저 예상하고 확인해. 이번에는 b = a.copy()로 다시 해보고, 두 경우의 id(a)와 id(b)를 비교해. 왜 결과가 달라졌는지 객체와 이름의 관계로 설명해봐.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 1

🔔 답글 알림 (로그인 필요)
로그인 — 댓글을 남기려면 로그인해 주세요.
  1. Knit J
    Knit J

    Exercise 했어. quest내용 다 소화를 못하겠네. 우선 좀 진도 나가고 다시 돌아와서 또 봐야할거 같아.

    a=[1,2,3] b=a b.append(4) print(a) [1, 2, 3, 4] print(b) [1, 2, 3, 4]

    a=[1,2,3] b=a.copy() b.append(4) print(a) [1, 2, 3] print(b) [1, 2, 3, 4]

    "b = a는 하나의 객체에 이름표를 하나 더 붙인 것이고, b = a.copy()는 아예 새로운 객체를 만들어 독립시켰기 때문에 결과가 다름"