프로그래밍을 처음 시작하는 분들에게는 변수의 개념이 다소 추상적으로 느껴질 수 있습니다. 이번 장에서는 파이썬 변수의 핵심 개념부터 실무에서 자주 마주치는 복사 문제까지 단계별로 알아보겠습니다. 당장 모든 내용을 완벽하게 이해하지 못해도 괜찮습니다. 파이썬을 학습하면서 자연스럽게 익혀나갈 수 있습니다.
변수 생성의 기본 원리
변수란 무엇인가?
파이썬에서 변수를 만드는 것은 매우 직관적입니다. 등호(=) 기호를 사용하여 데이터를 저장할 수 있습니다.
# 기본적인 변수 생성 예제
student_name = "김철수"
student_age = 25
student_grades = [85, 90, 78, 92]
다른 프로그래밍 언어와 달리 파이썬은 변수의 자료형을 미리 선언할 필요가 없습니다. 파이썬이 자동으로 데이터의 종류를 판단하여 적절한 자료형을 할당합니다.
# 자료형이 자동으로 결정되는 예제
number = 42 # 정수형 (int)
pi_value = 3.14159 # 실수형 (float)
message = "안녕하세요" # 문자열 (str)
is_student = True # 불린형 (bool)
# type() 함수로 자료형 확인
print(type(number)) # <class 'int'>
print(type(pi_value)) # <class 'float'>
print(type(message)) # <class 'str'>
print(type(is_student)) # <class 'bool'>
실습 예제: 간단한 학생 관리 프로그램
# 학생 정보를 저장하는 변수들
student_id = 2024001
student_name = "이영희"
korean_score = 88
english_score = 92
math_score = 79
# 평균 점수 계산
average_score = (korean_score + english_score + math_score) / 3
# 결과 출력
print(f"학번: {student_id}")
print(f"이름: {student_name}")
print(f"국어: {korean_score}점")
print(f"영어: {english_score}점")
print(f"수학: {math_score}점")
print(f"평균: {average_score:.2f}점")
# 학점 계산
if average_score >= 90:
grade = "A"
elif average_score >= 80:
grade = "B"
elif average_score >= 70:
grade = "C"
else:
grade = "F"
print(f"학점: {grade}")
파이썬 변수의 특성
변수와 객체의 관계
파이썬에서 변수는 실제로 객체를 가리키는 '참조'입니다. 이는 매우 중요한 개념으로, 다음 예제를 통해 살펴보겠습니다.
# 리스트 객체 생성과 변수 할당
shopping_list = ["사과", "바나나", "오렌지"]
# id() 함수로 메모리 주소 확인
print(f"shopping_list의 메모리 주소: {id(shopping_list)}")
# 같은 리스트를 가리키는 또 다른 변수
my_list = shopping_list
print(f"my_list의 메모리 주소: {id(my_list)}")
# 두 변수가 같은 객체를 가리키는지 확인
print(f"같은 객체를 가리키는가? {shopping_list is my_list}")
메모리 주소와 객체 참조 실습
# 숫자형 변수의 메모리 주소 확인
x = 100
y = 100
print(f"x의 주소: {id(x)}")
print(f"y의 주소: {id(y)}")
print(f"x와 y가 같은 객체? {x is y}")
# 문자열 변수의 메모리 주소 확인
name1 = "파이썬"
name2 = "파이썬"
print(f"name1의 주소: {id(name1)}")
print(f"name2의 주소: {id(name2)}")
print(f"name1과 name2가 같은 객체? {name1 is name2}")
데이터 복사 방법과 주의사항
리스트 복사 시 주의할 점
리스트를 다룰 때 가장 흔히 발생하는 실수가 바로 '얕은 복사' 문제입니다. 다음 예제를 통해 살펴보겠습니다.
# 위험한 리스트 할당 방법
original_scores = [85, 90, 78, 92, 88]
backup_scores = original_scores # 주의! 이는 복사가 아닙니다
print("초기 상태:")
print(f"원본 점수: {original_scores}")
print(f"백업 점수: {backup_scores}")
# 원본 리스트 수정
original_scores[0] = 95
original_scores.append(100)
print("\n원본 수정 후:")
print(f"원본 점수: {original_scores}")
print(f"백업 점수: {backup_scores}") # 백업도 함께 변경됨!
# 메모리 주소 확인
print(f"\n원본 주소: {id(original_scores)}")
print(f"백업 주소: {id(backup_scores)}")
print(f"같은 객체인가? {original_scores is backup_scores}")
실무 예제: 학급 성적 관리 시스템
# 학급 성적 관리에서 발생할 수 있는 문제 상황
class_a_scores = [82, 75, 90, 88, 93, 79, 85]
class_b_scores = class_a_scores # 잘못된 복사 방법
print("=== 초기 학급 성적 ===")
print(f"A반 성적: {class_a_scores}")
print(f"B반 성적: {class_b_scores}")
# A반 학생의 성적을 수정했는데...
class_a_scores[2] = 95 # 세 번째 학생 성적 수정
print("\n=== A반 성적 수정 후 ===")
print(f"A반 성적: {class_a_scores}")
print(f"B반 성적: {class_b_scores}") # B반 성적도 변경됨!
print(f"\n문제 원인: 같은 메모리 주소를 참조 {class_a_scores is class_b_scores}")
슬라이싱을 활용한 복사
기본 슬라이싱 복사 방법
리스트를 올바르게 복사하는 첫 번째 방법은 슬라이싱을 사용하는 것입니다.
# 슬라이싱을 이용한 올바른 복사
original_menu = ["김치찌개", "된장찌개", "부대찌개", "순두부찌개"]
copied_menu = original_menu[:] # 전체 슬라이싱으로 복사
print("=== 슬라이싱 복사 테스트 ===")
print(f"원본 메뉴: {original_menu}")
print(f"복사 메뉴: {copied_menu}")
# 원본 수정
original_menu.append("김치찌개")
original_menu[0] = "돼지김치찌개"
print("\n=== 원본 수정 후 ===")
print(f"원본 메뉴: {original_menu}")
print(f"복사 메뉴: {copied_menu}") # 영향받지 않음
# 서로 다른 객체인지 확인
print(f"\n다른 객체인가? {original_menu is not copied_menu}")
print(f"원본 주소: {id(original_menu)}")
print(f"복사 주소: {id(copied_menu)}")
슬라이싱 복사 실습 예제
# 온라인 쇼핑몰 장바구니 시스템
def create_shopping_cart():
"""새로운 장바구니를 생성하는 함수"""
return []
def copy_cart_items(source_cart):
"""장바구니 아이템을 복사하는 함수"""
return source_cart[:] # 슬라이싱으로 복사
# 메인 장바구니
main_cart = ["노트북", "마우스", "키보드", "모니터"]
print(f"메인 장바구니: {main_cart}")
# 임시 장바구니에 복사
temp_cart = copy_cart_items(main_cart)
print(f"임시 장바구니: {temp_cart}")
# 임시 장바구니에서 상품 제거 테스트
temp_cart.remove("모니터")
temp_cart.append("스피커")
print("\n=== 임시 장바구니 수정 후 ===")
print(f"메인 장바구니: {main_cart}") # 변경되지 않음
print(f"임시 장바구니: {temp_cart}")
# 할인 쿠폰 적용을 위한 백업
backup_cart = main_cart[:]
print(f"\n백업 장바구니: {backup_cart}")
copy 모듈 활용하기
copy 모듈의 기본 사용법
파이썬의 copy 모듈을 사용하면 더욱 명확하게 객체를 복사할 수 있습니다.
from copy import copy
# copy 모듈을 사용한 복사
student_info = {
"name": "박민수",
"age": 22,
"subjects": ["수학", "영어", "과학"],
"grades": [88, 92, 85]
}
# copy() 함수로 복사
student_backup = copy(student_info)
print("=== copy 모듈 사용 예제 ===")
print(f"원본 정보: {student_info}")
print(f"백업 정보: {student_backup}")
# 리스트 내부 요소 수정
student_info["subjects"].append("체육")
student_info["grades"][0] = 90
print("\n=== 원본 수정 후 ===")
print(f"원본 정보: {student_info}")
print(f"백업 정보: {student_backup}") # 내부 리스트는 영향을 받음
print(f"\n서로 다른 객체? {student_info is not student_backup}")
깊은 복사(Deep Copy) 사용하기
from copy import deepcopy
# 중첩된 리스트 구조
class_schedule = [
["월요일", ["수학", "영어", "과학"]],
["화요일", ["국어", "체육", "음악"]],
["수요일", ["미술", "역사", "수학"]]
]
# 얕은 복사 vs 깊은 복사
shallow_copy = copy(class_schedule)
deep_copy = deepcopy(class_schedule)
print("=== 깊은 복사 비교 ===")
print(f"원본: {class_schedule}")
print(f"얕은 복사: {shallow_copy}")
print(f"깊은 복사: {deep_copy}")
# 내부 리스트 수정
class_schedule[0][1].append("프로그래밍")
print("\n=== 원본 수정 후 ===")
print(f"원본: {class_schedule}")
print(f"얕은 복사: {shallow_copy}") # 영향받음
print(f"깊은 복사: {deep_copy}") # 영향받지 않음
리스트 자체 copy() 메서드 사용
# 리스트 객체의 copy() 메서드 활용
game_inventory = ["검", "방패", "포션", "마법서", "화살"]
backup_inventory = game_inventory.copy() # 내장 copy() 메서드
print("=== 게임 인벤토리 관리 ===")
print(f"메인 인벤토리: {game_inventory}")
print(f"백업 인벤토리: {backup_inventory}")
# 아이템 사용 시뮬레이션
game_inventory.remove("포션") # 포션 사용
game_inventory.append("빨간포션") # 새 포션 획득
print("\n=== 아이템 사용 후 ===")
print(f"메인 인벤토리: {game_inventory}")
print(f"백업 인벤토리: {backup_inventory}") # 변경되지 않음
# 인벤토리 복원 기능
def restore_inventory(original, backup):
"""백업에서 인벤토리를 복원하는 함수"""
return backup.copy()
restored_inventory = restore_inventory(game_inventory, backup_inventory)
print(f"\n복원된 인벤토리: {restored_inventory}")
다양한 변수 할당 기법
튜플을 이용한 다중 할당
파이썬에서는 여러 변수에 값을 동시에 할당하는 다양한 방법을 제공합니다.
# 튜플을 이용한 다중 할당
student_data = ("홍길동", 20, "컴퓨터공학과")
name, age, major = student_data
print("=== 튜플 언패킹 ===")
print(f"이름: {name}")
print(f"나이: {age}")
print(f"전공: {major}")
# 괄호 없이도 가능
x, y, z = 10, 20, 30
print(f"\nx={x}, y={y}, z={z}")
# 리스트를 이용한 다중 할당
colors = ["빨강", "초록", "파랑"]
red, green, blue = colors
print(f"\n색상: {red}, {green}, {blue}")
변수 값 교환하기
# 파이썬만의 우아한 변수 교환 방법
player1_score = 150
player2_score = 230
print("=== 점수 교환 전 ===")
print(f"플레이어1: {player1_score}점")
print(f"플레이어2: {player2_score}점")
# 한 줄로 값 교환
player1_score, player2_score = player2_score, player1_score
print("\n=== 점수 교환 후 ===")
print(f"플레이어1: {player1_score}점")
print(f"플레이어2: {player2_score}점")
# 여러 변수 동시 교환
a, b, c = 1, 2, 3
print(f"\n교환 전: a={a}, b={b}, c={c}")
a, b, c = c, a, b # 순환 교환
print(f"교환 후: a={a}, b={b}, c={c}")
같은 값을 여러 변수에 할당
# 동일한 값을 여러 변수에 할당
default_hp = default_mp = default_exp = 100
print("=== 게임 캐릭터 초기값 ===")
print(f"체력: {default_hp}")
print(f"마력: {default_mp}")
print(f"경험치: {default_exp}")
# 주의사항: 리스트와 같은 가변 객체의 경우
# 잘못된 방법
wrong_list1 = wrong_list2 = []
wrong_list1.append("아이템")
print(f"\n잘못된 예: {wrong_list1}, {wrong_list2}") # 둘 다 변경됨
# 올바른 방법
correct_list1 = []
correct_list2 = []
correct_list1.append("아이템")
print(f"올바른 예: {correct_list1}, {correct_list2}") # 하나만 변경됨
실전 예제: 간단한 텍스트 RPG 게임
# 텍스트 RPG 게임에서 변수 활용 예제
def create_character():
"""새로운 캐릭터를 생성하는 함수"""
# 기본 스탯 설정
name, level, hp, mp = "용사", 1, 100, 50
# 인벤토리 초기화 (올바른 복사 방법 사용)
basic_items = ["나무검", "가죽갑옷", "빵"]
inventory = basic_items.copy()
# 캐릭터 정보 딕셔너리
character = {
"name": name,
"level": level,
"hp": hp,
"mp": mp,
"inventory": inventory,
"location": "마을"
}
return character
def battle_system(hero, monster_hp):
"""간단한 전투 시스템"""
print(f"\n=== 전투 시작! ===")
print(f"{hero['name']}의 체력: {hero['hp']}")
print(f"몬스터 체력: {monster_hp}")
# 전투 시뮬레이션
round_count = 1
hero_hp = hero['hp'] # 복사하여 원본 보호
while hero_hp > 0 and monster_hp > 0:
print(f"\n--- {round_count}라운드 ---")
# 플레이어 공격
import random
damage = random.randint(15, 25)
monster_hp -= damage
print(f"{hero['name']}이 {damage} 데미지를 입혔습니다!")
if monster_hp <= 0:
print("몬스터를 물리쳤습니다!")
break
# 몬스터 공격
monster_damage = random.randint(10, 20)
hero_hp -= monster_damage
print(f"몬스터가 {monster_damage} 데미지를 입혔습니다!")
print(f"{hero['name']} 체력: {hero_hp}")
print(f"몬스터 체력: {monster_hp}")
round_count += 1
if round_count > 10: # 무한 루프 방지
break
# 전투 결과 반영
if hero_hp > 0:
hero['hp'] = hero_hp
return True
else:
print(f"{hero['name']}이 쓰러졌습니다...")
return False
# 게임 실행
print("=== 텍스트 RPG 게임 ===")
main_character = create_character()
print("캐릭터 정보:")
for key, value in main_character.items():
print(f"{key}: {value}")
# 전투 테스트
battle_result = battle_system(main_character, 80)
if battle_result:
print(f"\n전투 후 {main_character['name']}의 체력: {main_character['hp']}")
else:
print("\n게임 오버!")
변수 할당 고급 기법
# 확장된 언패킹 (Python 3.0 이상)
scores = [95, 87, 92, 78, 85, 90, 83]
# 첫 번째, 마지막과 나머지 분리
first, *middle, last = scores
print(f"첫 번째 점수: {first}")
print(f"마지막 점수: {last}")
print(f"중간 점수들: {middle}")
# 처음 두 개와 나머지 분리
top1, top2, *others = scores
print(f"\n1등 점수: {top1}")
print(f"2등 점수: {top2}")
print(f"나머지 점수: {others}")
# 함수에서 여러 값 반환하기
def get_student_info():
"""학생 정보를 반환하는 함수"""
return "김영수", 85, 90, 78 # 이름, 수학, 영어, 과학
# 여러 값을 한 번에 받기
student_name, math_score, eng_score, sci_score = get_student_info()
print(f"\n학생: {student_name}")
print(f"수학: {math_score}, 영어: {eng_score}, 과학: {sci_score}")
# 딕셔너리 키-값 언패킹
user_data = {"username": "coder123", "email": "coder@example.com", "age": 25}
for key, value in user_data.items():
print(f"{key}: {value}")
이상으로 파이썬 변수와 메모리 관리에 대한 종합적인 가이드를 마칩니다. 변수의 기본 개념부터 복사 메커니즘, 그리고 다양한 할당 기법까지 실무에서 자주 사용되는 패턴들을 다뤘습니다. 특히 리스트 복사 시 발생할 수 있는 문제점과 해결 방법을 충분히 이해하고 넘어가시기 바랍니다. 이러한 개념들은 파이썬 프로그래밍에서 매우 중요한 기초가 됩니다.
'파이썬' 카테고리의 다른 글
10. 파이썬 반복문 마스터하기: while문 완전 정복 (1) | 2025.05.27 |
---|---|
9. 파이썬 조건문 (IF문) 완벽히 이해하기. 예제로 배우는 코딩공부. (0) | 2025.05.27 |
7. 파이썬 불린(Boolean) 자료형에 대해서 공부하기 (0) | 2025.05.26 |
6. 파이썬 집합(SET) 자료형. 여러 예제로 깊게 이해하기. (0) | 2025.05.25 |
5. 파이썬 딕셔너리 초보부터 중수까지 완벽한 예제로 공부하자. (0) | 2025.05.25 |