파이썬 / / 2025. 5. 26. 23:04

8. 파이썬 변수와 메모리 관리 완벽 가이드

반응형

파이썬-변수-메모리관리

 

 

 

프로그래밍을 처음 시작하는 분들에게는 변수의 개념이 다소 추상적으로 느껴질 수 있습니다. 이번 장에서는 파이썬 변수의 핵심 개념부터 실무에서 자주 마주치는 복사 문제까지 단계별로 알아보겠습니다. 당장 모든 내용을 완벽하게 이해하지 못해도 괜찮습니다. 파이썬을 학습하면서 자연스럽게 익혀나갈 수 있습니다.

변수 생성의 기본 원리

 

변수란 무엇인가?

파이썬에서 변수를 만드는 것은 매우 직관적입니다. 등호(=) 기호를 사용하여 데이터를 저장할 수 있습니다.

# 기본적인 변수 생성 예제
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}")

 

 

이상으로 파이썬 변수와 메모리 관리에 대한 종합적인 가이드를 마칩니다. 변수의 기본 개념부터 복사 메커니즘, 그리고 다양한 할당 기법까지 실무에서 자주 사용되는 패턴들을 다뤘습니다. 특히 리스트 복사 시 발생할 수 있는 문제점과 해결 방법을 충분히 이해하고 넘어가시기 바랍니다. 이러한 개념들은 파이썬 프로그래밍에서 매우 중요한 기초가 됩니다.

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유