누구나 로고
オンライン講座企業・団体研修ライブラリトレンドインサイト無料コースお知らせお問い合わせ採用サイト

カスタマーサポート

  • お知らせ
  • よくある質問
  • footer.contact
  • コミュニティ

ご利用案内

  • 利用規約
  • プライバシーポリシー
  • 返金ポリシー

サービス

  • 会社紹介
  • 新規登録
  • 新着コース
  • 無料コース
누구나 로고
利用規約プライバシーポリシー返金ポリシー

상호명: NUGUNA  |  대표자: 정우진  |  사업자등록번호: 392-32-01817  |  통신판매업신고: 제 2026-서울양천-0564 호

주소: 서울특별시 양천구 목동서로 100, 309동 1006호  |  이메일: nugunapass@gmail.com  |  전화: 010-6395-3043

© 2026 NUGUNA. All rights reserved.

KB예금주인증
ホームコースライブラリお問い合わせMY
관리자
모든 강의를 무료로 볼 수 있어요. 회원가입 없이도 학습 가능합니다.
로그인무료 회원가입
강좌파이썬 기초 40강 — 처음 배우는 프로그래밍5강

5강 / 전체 6강

변수란 무엇인가 — 이름표, 객체, 참조의 삼각관계

9분 읽기 조회 54

C언어 변수와 파이썬 변수의 근본적 차이를 이해하고, 참조와 메모리 관리를 파악한다.

🎯 이 강의에서 배우는 것

파이썬을 배울 때 가장 먼저 만나는 개념이 변수입니다. 그런데 파이썬의 변수는 여러분이 C나 Java에서 배웠거나 상상하는 변수와 근본적으로 다릅니다. 이 차이를 제대로 이해하지 못하면, 앞으로 배울 리스트 복사, 함수 인자 전달, 가비지 컬렉션 등에서 계속 혼란을 겪게 됩니다. 이 강의는 그 혼란의 근원을 완전히 제거합니다.

📦 박스 모델 vs 이름표 모델

C 언어에서 변수를 선언하면 메모리에 "박스"가 만들어집니다:

/* C 언어 */
/* int x = 5; */
/* 스택 메모리에 4바이트 박스가 생기고, 그 안에 5가 저장됨 */
/* x라는 이름 = 그 메모리 위치(주소) */
/* x = 10; 하면 같은 박스에 10을 덮어씀 */

# 파이썬에서는 완전히 다릅니다
x = 5
# 1. 힙 메모리 어딘가에 "정수 5" 객체가 생성됨
# 2. x라는 이름표(참조/포인터)가 그 객체를 가리킴
# 3. x라는 이름표에는 타입 정보가 없음 — 객체에 있음

x = 10
# 이전의 정수 5 객체는 그대로 있고
# x라는 이름표가 새로운 "정수 10" 객체를 가리킴
# (이전 객체의 참조 카운트가 줄어들고, 0이 되면 메모리 해제)

이것을 이름표 모델(name-binding model) 또는 참조 모델(reference model)이라고 합니다. 파이썬에서 =는 "값을 저장한다"가 아니라 "이름을 객체에 바인딩한다"는 의미입니다.

🔍 객체의 3요소: id, type, value

파이썬의 모든 객체는 세 가지 요소를 가집니다:

x = 42

# 1. id: 객체의 고유 식별자 (CPython에서는 메모리 주소)
print(id(x))        # 예: 140234567890

# 2. type: 객체의 타입
print(type(x))      # <class 'int'>

# 3. value: 객체가 담고 있는 값
print(x)            # 42

# 세 가지를 한번에 확인
print(f"id={id(x)}, type={type(x).__name__}, value={x}")
# id=140234567890, type=int, value=42

id()가 반환하는 값은 CPython에서 실제 메모리 주소입니다(다른 구현체에서는 다를 수 있습니다). 두 변수가 같은 객체를 가리키면 id()가 같습니다:

a = [1, 2, 3]
b = a           # b는 a와 같은 리스트 객체를 가리킴

print(id(a))    # 예: 140000000001
print(id(b))    # 예: 140000000001 (동일!)
print(a is b)   # True

b.append(4)
print(a)        # [1, 2, 3, 4] — a도 변경됨!
# 이유: a와 b가 같은 객체를 공유하기 때문

🆚 is vs == — 자주 혼동하는 두 연산자

파이썬에서 비교 연산자가 두 가지인 이유를 정확히 이해해야 합니다:

# == : 값이 같은가? (동등성, equality)
# is : 같은 객체인가? (동일성, identity)

a = [1, 2, 3]
b = [1, 2, 3]

print(a == b)   # True  — 값은 같음
print(a is b)   # False — 다른 객체 (다른 메모리 주소)

c = a
print(a is c)   # True  — 같은 객체

소형 정수 캐싱(-5~256): CPython은 자주 사용되는 -5에서 256까지의 정수 객체를 미리 만들어 재사용합니다. 이 범위에서는 is가 ==처럼 동작하는 것처럼 보입니다:

# 소형 정수 캐싱 예시
a = 100
b = 100
print(a is b)   # True  — 같은 캐시된 객체

a = 1000
b = 1000
print(a is b)   # False — 캐시 범위 초과, 별도 객체 생성

# 인터랙티브 셸에서는 같은 블록의 리터럴을 최적화할 수 있어
# 결과가 다를 수 있음 (구현 세부사항)

핵심 규칙: is는 None, True, False와 비교할 때만 사용하세요. 숫자나 문자열 값 비교에는 항상 ==를 사용하세요. is를 일반 값 비교에 쓰면 소형 정수 캐싱 때문에 버그가 발생할 수 있습니다.

# 올바른 None 확인
def process(value=None):
    if value is None:       # 권장
        return "기본값 사용"
    if value is not None:   # 권장
        return f"값: {value}"

# 틀린 방법 (기능은 같지만 명시성이 떨어짐)
if value == None:   # None과 ==로 비교 — 작동은 하지만 관례에 어긋남
    pass

🔢 참조 카운팅 — 메모리가 해제되는 순간

CPython은 참조 카운팅(reference counting)으로 메모리를 관리합니다. 각 객체는 자신을 가리키는 참조(이름표)의 수를 기록합니다:

import sys

x = [1, 2, 3]  # 리스트 객체 생성, 참조 카운트 = 1
print(sys.getrefcount(x))  # 2 — getrefcount 인자로 전달되면서 임시 참조 +1

y = x          # 참조 카운트 = 2
z = x          # 참조 카운트 = 3
print(sys.getrefcount(x))  # 4 (getrefcount 임시 참조 포함)

del y          # 참조 카운트 = 2
del z          # 참조 카운트 = 1
del x          # 참조 카운트 = 0 → 메모리 즉시 해제!
# 이 시점에서 [1, 2, 3] 객체는 메모리에서 사라짐

del x는 x라는 이름표를 제거하는 것이지, 객체를 직접 삭제하는 것이 아닙니다. 참조 카운트가 0이 될 때 객체가 해제됩니다. 다른 참조가 남아있으면 객체는 계속 살아있습니다.

참조 카운팅이 즉각적인 메모리 해제를 가능하게 하는 이유:

  • 마지막 참조가 사라지는 순간 즉시 __del__ 메서드 호출 및 메모리 해제
  • 파일 핸들, 네트워크 연결 등 자원이 예측 가능한 시점에 해제됨
  • Java의 GC처럼 "언젠가 해제될 것"이 아니라 "즉시 해제"

♻️ 순환 참조와 세대별 가비지 컬렉션

참조 카운팅의 한계: 순환 참조(circular reference)는 해제할 수 없습니다:

import gc

# 순환 참조 만들기
a = []
a.append(a)  # a가 자기 자신을 참조
# a의 참조 카운트는 최소 2 (a라는 이름 + a 자신의 내부 참조)
# del a를 해도 내부 참조 때문에 참조 카운트가 0이 되지 않음!

del a
# a라는 이름이 사라져도 내부 순환 참조로 인해 메모리 미해제

# CPython의 cyclic garbage collector가 주기적으로 처리
gc.collect()  # 즉시 순환 참조 수집 요청

# GC 상태 확인
print(gc.get_count())    # (세대0 카운터, 세대1 카운터, 세대2 카운터)
print(gc.get_threshold()) # (700, 10, 10) — 기본 임계값

CPython의 세대별 GC(generational GC)는 객체를 세 세대(generation)로 분류합니다. 새로 생성된 객체는 세대 0에 속하고, GC 사이클에서 살아남으면 세대 1, 2로 승격됩니다. 오래된 세대일수록 GC 빈도가 낮아 성능 영향이 줄어듭니다.

🔄 다중 할당과 변수 교환

파이썬에서 변수를 교환하는 방법이 얼마나 우아한지 보여드립니다:

# C 언어에서의 변수 교환 (임시 변수 필요)
# int temp = a;
# a = b;
# b = temp;

# 파이썬에서는 임시 변수 없이
a = 10
b = 20
a, b = b, a  # 단 한 줄!
print(a, b)   # 20 10

# 이것이 작동하는 내부 원리:
# 1. 오른쪽 b, a를 튜플로 패킹: (20, 10)
# 2. 왼쪽 a, b에 튜플 언패킹: a=20, b=10
# 임시 튜플이 생성되지만, 매우 빠르고 우아한 방식

# 다중 할당
x = y = z = 0  # x, y, z 모두 같은 정수 0 객체를 가리킴
print(x is y)   # True (소형 정수 캐싱으로 같은 객체)

# 주의: 가변 객체로 다중 할당하면 위험
a = b = []      # a와 b가 같은 리스트를 공유!
a.append(1)
print(b)        # [1] — b도 변경됨!

# 올바른 방법: 각각 독립적인 리스트 생성
a = []
b = []
a.append(1)
print(b)        # [] — b는 영향 없음

언패킹(unpacking)은 파이썬의 강력한 기능입니다:

# 기본 언패킹
first, second, third = [10, 20, 30]
print(first, second, third)  # 10 20 30

# 확장 언패킹 (Python 3+)
first, *rest = [1, 2, 3, 4, 5]
print(first)   # 1
print(rest)    # [2, 3, 4, 5]

*head, last = [1, 2, 3, 4, 5]
print(head)    # [1, 2, 3, 4]
print(last)    # 5

first, *middle, last = [1, 2, 3, 4, 5]
print(first, middle, last)  # 1 [2, 3, 4] 5

⚠️ 자주 하는 실수

  • 할당이 복사라고 생각함: b = a는 a의 복사본을 만드는 것이 아니라 같은 객체에 이름표를 하나 더 붙이는 것입니다. 가변 객체에서 특히 주의하세요.
  • is를 값 비교에 사용: 소형 정수 캐싱 때문에 작은 정수에서는 우연히 작동하지만, 큰 정수나 다른 객체에서는 예상과 다른 결과가 나옵니다.
  • 다중 할당으로 가변 객체 공유: a = b = []는 같은 리스트를 공유합니다. 각각 독립적인 리스트가 필요하면 a = []; b = []처럼 따로 생성하세요.

📝 정리 및 다음 강의 예고

  • 파이썬 변수는 "박스"가 아니라 객체를 가리키는 "이름표"입니다.
  • 모든 파이썬 객체는 id(메모리 주소), type(타입), value(값)를 가집니다.
  • is는 동일 객체 여부, ==는 동등한 값 여부를 비교합니다.
  • 참조 카운팅으로 메모리를 즉시 해제하고, 순환 참조는 세대별 GC가 처리합니다.
  • a, b = b, a는 튜플 패킹·언패킹으로 임시 변수 없이 교환합니다.

다음 강의: 파이썬 정수(int)의 내부를 완전히 해부합니다. 왜 파이썬 정수는 오버플로우가 없는지, 비트 연산은 어떻게 작동하는지, 그리고 / 와 // 나눗셈의 차이를 배웁니다.

관련 주제

  • 변수 이름표 모델
  • is vs == 차이
  • 소형 정수 캐싱
  • 참조 카운팅
  • 순환 참조와 가비지컬렉션
  • 다중 할당·변수 교환
  • 개발·프로그래밍
  • 개발·프로그래밍 강의
  • 파이썬 기초 40강 — 처음 배우는 프로그래밍
  • 무료강의
  • 무료 온라인 강의
  • NUGUNA
  • 누구나

목차

이전 4강첫 프로그램 — print()와 input()으로 배우는 I/O 원리
다음 6강 정수(int) 완전 해부 — 임의 정밀도, 비트 연산, 정수 연산의 함정
1.컴퓨터는 어떻게 생각하는가 — 0과 1에서 프로그램까지
2.파이썬 생태계와 개발 환경 완전 설정
3.파이썬의 실행 모델 — 바이트코드, PVM, GIL
4.첫 프로그램 — print()와 input()으로 배우는 I/O 원리
5.변수란 무엇인가 — 이름표, 객체, 참조의 삼각관계
6.정수(int) 완전 해부 — 임의 정밀도, 비트 연산, 정수 연산의 함정