해시 연습 4

류한선·2025년 7월 3일

실기연습-2

목록 보기
78/95

좋아! 지금부터 요청한 4가지 트랩 주제를 각각 실전 문제처럼 구성해서

  1. 문제
  2. 선택지
  3. 정답
  4. 줄 단위 디버깅 해설
  5. 요약 포인트
    까지 초보자도 절대 헷갈리지 않게 완전 해부해줄게.

🧨 1. mutable default argument 트랩

def add_item(item, items=[]):
    items.append(item)
    return items

print(add_item("a"))
print(add_item("b"))

보기:

A. ['a']['b']
B. ['a']['a', 'b']
C. ['a', 'b']['a', 'b']
D. 에러 발생


✅ 정답: B. ['a'], ['a', 'b']


🔍 해설

def add_item(item, items=[]):  # ❗️ 리스트는 함수 정의 시 한 번만 생성됨!
    items.append(item)
    return items
  • 여기서 items함수 호출할 때마다 새로 만들어지지 않고,
    처음 정의된 그 리스트가 계속 재사용됨
  • 즉, 모든 호출이 같은 리스트 객체를 공유함
# 첫 호출: add_item("a") → items = ['a']
# 두 번째 호출: add_item("b") → items = ['a', 'b'] (같은 리스트에 추가됨)

✔️ 안전한 코드 방식:

def add_item(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

💡 요약:

  • 가변 객체(list, dict)를 기본 인자로 쓰면 공유된다!
  • None으로 초기화 후 내부에서 새로 만들어야 안전

🧨 2. dict 순서 보장 관련 트랩 (파이썬 3.6+)

d = {}
d['banana'] = 3
d['apple'] = 4
d['pear'] = 1

print(list(d.keys()))

보기:

A. ['apple', 'banana', 'pear']
B. ['pear', 'banana', 'apple']
C. ['banana', 'apple', 'pear']
D. 순서 보장 안됨


✅ 정답: C. ['banana', 'apple', 'pear']


🔍 해설

  • 파이썬 3.6부터 CPython 구현에서 dict는 삽입 순서 유지
  • 그리고 파이썬 3.7부터는 공식 사양으로 순서 보장

즉, 삽입 순서대로:

d['banana'] → 첫 번째
d['apple'] → 두 번째
d['pear'] → 세 번째

💡 요약:

버전순서 보장 여부
3.5 이하❌ 불보장
3.6✅ CPython만 보장 (비공식)
3.7 이상✅ 명시적으로 보장

🧨 3. *args, `kwargs` 꼬기 트랩**

def func(a, *args, **kwargs):
    print(a, args, kwargs)

func(1, 2, 3, x=4, y=5)

보기:

A. 1 (2, 3) {'x': 4, 'y': 5}
B. 1 (3, 2) {'x': 4, 'y': 5}
C. 1 [2, 3] {'x': 4, 'y': 5}
D. 에러 발생


✅ 정답: A. 1 (2, 3) {'x': 4, 'y': 5}


🔍 해설

def func(a, *args, **kwargs):
    print(a, args, kwargs)
  • a → 고정 인자, 가장 먼저 받음 → 1
  • *args → 나머지 위치 인자 → (2, 3) (튜플로 받음)
  • **kwargs → 키워드 인자 → {'x': 4, 'y': 5} (딕셔너리로 받음)
func(1, 2, 3, x=4, y=5)
# a = 1
# args = (2, 3)
# kwargs = {'x': 4, 'y': 5}

💡 요약:

  • *args는 위치 인자 묶음 → 튜플로 전달됨
  • **kwargs는 키워드 인자 묶음 → 딕셔너리로 전달됨

🧨 4. 클래스 변수 vs 인스턴스 변수 트랩

class MyClass:
    items = []

    def __init__(self, value):
        self.value = value
        MyClass.items.append(value)

a = MyClass(1)
b = MyClass(2)

print(a.items)
print(b.items)
print(MyClass.items)

보기:

A. [1], [2], [1, 2]
B. [1, 2], [1, 2], [1, 2]
C. [1], [1, 2], [1, 2]
D. 에러 발생


✅ 정답: B. [1, 2], [1, 2], [1, 2]


🔍 해설

class MyClass:
    items = []  # 클래스 변수 (공유됨)

    def __init__(self, value):
        self.value = value
        MyClass.items.append(value)  # 클래스 변수에 직접 추가
  • 클래스 변수는 모든 인스턴스가 공유
  • 따라서 a.items, b.items, MyClass.items 전부 동일

✔️ 만약 인스턴스별로 items를 다르게 하려면?

def __init__(self, value):
    self.value = value
    self.items = []
    self.items.append(value)
  • 이 경우 self.items는 인스턴스 변수 → 각 인스턴스마다 따로 생성됨

💡 요약:

변수 위치적용 대상공유 여부
클래스 변수 (MyClass.items)클래스 전체✅ 공유
인스턴스 변수 (self.items)개별 객체❌ 공유 안 함

📘 전체 요약표

트랩 유형핵심 포인트안전하게 쓰는 방법
기본 인자에 가변 객체값이 공유됨None + 내부 생성
dict 순서 보장3.7부터 삽입 순서 유지버전 확인
*args, **kwargs위치/키워드 인자 분리순서만 주의하면 안전
클래스 vs 인스턴스 변수클래스 변수는 공유됨인스턴스마다 다르게 하려면 self. 사용

다음 트랩 주제로는 이런 것도 있어:

  • try-except-finally에서 return 충돌 트랩
  • with open() 안에서 파일 처리 이상 동작
  • 변수 shadowing (같은 이름이 다른 의미로 덮여쓰기)
  • 제너레이터(yield)와 next 트랩
  • 리스트 얕은 복사 vs 깊은 복사 트랩

원하는 거 있으면 이어서 출제해줄게!

훌륭한 선택이야! 지금부터 요청한 4가지 트랩을 하나하나 실전 문제 형식으로 내고,
각 줄마다 디버깅하듯 완전히 해부해서 설명할게.


🔥 1. try-except-finally에서 return 충돌 트랩

def test():
    try:
        return "try"
    except:
        return "except"
    finally:
        return "finally"

print(test())

보기:

A. "try"
B. "except"
C. "finally"
D. 에러 발생


✅ 정답: C. "finally"


🔍 해설:

try:
    return "try"       # ✅ 실행됨
except:
    return "except"    # ❌ 실행되지 않음
finally:
    return "finally"   # ❗️ return을 **덮어씀**
  • finally 블록은 예외가 발생하든 말든 반드시 실행됨
  • 그리고 finally 안에 return이 있으면, 앞선 tryreturn덮어버림

💡 요약:

  • finally는 무조건 실행되고
  • return도 강제로 덮어쓴다
  • 절대 무심코 finallyreturn 쓰지 말 것

📁 2. with open() 안에서 파일 처리 트랩

with open("data.txt", "w") as f:
    f.write("hello")

print(f.closed)

보기:

A. True
B. False
C. 에러 발생
D. "hello"


✅ 정답: A. True


🔍 해설:

with open("data.txt", "w") as f:
    f.write("hello")
  • with 블록이 끝나면 → f.close()가 자동 호출됨
  • 그래서 f.closedTrue

📌 블록 끝나는 순간 자동 정리되는 구조를 컨텍스트 매니저라고 함


💡 요약:

  • with open(...) as f: → 끝나면 자동으로 닫힘
  • f.closed → 블록 바깥에서는 항상 True

🔁 3. 제너레이터 (yield) + next() 트랩

def gen():
    yield 1
    yield 2
    return 3

g = gen()
print(next(g))
print(next(g))
print(next(g))

보기:

A. 1 2 3
B. 1 2 StopIteration
C. 1 2 None
D. 에러 발생


✅ 정답: D. 에러 발생 (StopIteration 예외)


🔍 해설:

def gen():
    yield 1       # ✅ 첫 next() → 1
    yield 2       # ✅ 두 번째 next() → 2
    return 3      # ❌ 세 번째 next() → StopIteration 발생
  • 제너레이터가 return을 만나면 → StopIteration 예외 발생
  • 이 때 return 3next()에서는 출력되지 않음
  • next(g)를 3번 호출하므로 마지막에 에러 발생

💡 요약:

  • yield로 값을 순차적으로 내보냄
  • return끝낸다는 뜻이고, StopIteration을 일으킨다

🧬 4. 리스트 얕은 복사 vs 깊은 복사 트랩

import copy

a = [[1, 2], [3, 4]]
b = copy.copy(a)
b[0][0] = 99

print(a)

보기:

A. [[1, 2], [3, 4]]
B. [[99, 2], [3, 4]]
C. [[1, 2], [99, 4]]
D. [[99, 2], [99, 4]]


✅ 정답: B. [[99, 2], [3, 4]]


🔍 해설:

b = copy.copy(a)   # ✅ 얕은 복사 (1단계만 복사)
b[0][0] = 99       # ❗️ 내부 리스트는 공유 → a[0][0]도 바뀜

📌 얕은 복사:

  • b는 새 리스트지만
  • 내부 요소인 [1, 2], [3, 4]a공유
a = [[1, 2], [3, 4]]
        ↑
       공유됨

b[0][0] = 99 → a[0][0]도 변경됨

✔️ 깊은 복사법:

b = copy.deepcopy(a)

💡 요약:

복사 방식깊이내부 리스트 공유 여부
copy.copy()얕은✅ 공유됨
copy.deepcopy()깊은❌ 공유 안 함

📘 전체 요약 정리

트랩 유형핵심 실수안전한 이해
try-finally returnfinally가 return을 덮어씀return은 finally에 두지 마라
with open자동으로 닫힘f.closed == True
제너레이터return → StopIterationnext()는 yield까지만
얕은 복사내부 객체 공유됨deepcopy() 사용

좋아! 요청한 4가지 트랩 유형은 모두 정보처리기사 실기나 현업에서 기본기가 흔들리면 쉽게 틀리는 주제야. 각 주제별로 다음 구조로 설명할게:

  • 문제 출제
  • 보기
  • 정답
  • 각 줄 상세 해설
  • 핵심 요약

🔥 1. global vs nonlocal 트랩

x = "global"

def outer():
    x = "outer"
    def inner():
        nonlocal x
        x = "inner"
    inner()
    print(x)

outer()
print(x)

보기:

A. outer, global
B. inner, global
C. inner, inner
D. 에러 발생


✅ 정답: B. inner, global


🔍 줄별 해설

x = "global"            # 전역 x

def outer():
    x = "outer"         # outer의 지역 변수 x

    def inner():
        nonlocal x      # 바깥 함수(outer)의 x를 사용
        x = "inner"     # outer의 x를 "inner"로 변경

    inner()
    print(x)            # → inner로 바뀐 x 출력됨

outer()                 # → inner 출력
print(x)                # 전역 x는 바뀌지 않음 → global 출력

💡 요약

키워드영향 범위
global전역 변수에 직접 접근
nonlocal가장 가까운 바깥 함수의 변수에 접근 (지역 → 바깥 지역)

🌀 2. 리스트 중첩 구조 슬라이스 트랩

a = [[1, 2], [3, 4], [5, 6]]
b = a[:]
b[0][0] = 99
print(a)

보기:

A. [[1, 2], [3, 4], [5, 6]]
B. [[99, 2], [3, 4], [5, 6]]
C. [[1, 2], [99, 4], [5, 6]]
D. 에러 발생


✅ 정답: B. [[99, 2], [3, 4], [5, 6]]


🔍 해설

a = [[1, 2], [3, 4], [5, 6]]
b = a[:]             # 슬라이스 복사는 **얕은 복사**

b[0][0] = 99         # b[0]은 a[0]과 같은 리스트 → 원본도 변경됨

📌 슬라이스는 리스트 자체만 새로 만들고, 내부 요소는 그대로 참조

a ───► [ [1, 2], [3, 4], [5, 6] ]
         ▲      ▲
         │      └── 공유됨
         └── b[0]도 같은 참조

💡 요약:

  • a[:]은 얕은 복사 → 내부 리스트는 그대로 공유됨
  • 깊은 복사는 copy.deepcopy(a)

🪞 3. __str__ vs __repr__ 혼동 트랩

class MyClass:
    def __str__(self):
        return "STR"
    def __repr__(self):
        return "REPR"

obj = MyClass()
print(obj)
print([obj])

보기:

A. STR, [STR]
B. STR, [REPR]
C. REPR, [REPR]
D. STR, [MyClass object at ...]


✅ 정답: B. STR, [REPR]


🔍 해설

print(obj)     # __str__() 호출 → STR
print([obj])   # 리스트 출력은 내부 요소에 대해 __repr__() 호출 → REPR
  • print(obj)는 사람이 보기 좋은 문자열__str__
  • print([obj])는 개발자 디버깅용 → __repr__

💡 요약

메서드호출되는 상황
__str__print(obj), str(obj)
__repr__리스트/딕셔너리 출력, repr(obj)

🧱 4. default argument로 dict 쓰기 트랩

def add_entry(key, value, store={}):
    store[key] = value
    return store

print(add_entry("a", 1))
print(add_entry("b", 2))

보기:

A. {'a': 1}, {'b': 2}
B. {'a': 1}, {'a': 1, 'b': 2}
C. {'b': 2}, {'b': 2}
D. 에러 발생


✅ 정답: B. {'a': 1}, {'a': 1, 'b': 2}


🔍 해설

  • store={}는 함수 정의 시에 딱 한 번만 생성
  • 여러 번 호출하면 그 딕셔너리를 계속 공유
# 첫 호출:
store = {'a': 1}

# 두 번째 호출:
store = {'a': 1, 'b': 2}   # 같은 딕셔너리에 추가됨

✔️ 안전한 방식:

def add_entry(key, value, store=None):
    if store is None:
        store = {}
    store[key] = value
    return store

💡 요약:

  • dict, list 같은 가변 객체는 기본 인자로 쓰면 안 됨
  • 항상 None으로 초기화하고 내부에서 새로 만들어야 안전

✅ 전체 요약표

트랩 유형핵심 실수안전한 이해
global vs nonlocal헷갈리는 범위nonlocal은 바깥 함수, global은 전역
슬라이스 복사얕은 복사라 내부 공유됨깊은 복사는 copy.deepcopy()
__str__ vs __repr__사람이 볼 때와 코드 출력 다름print()str, 컬렉션은 repr
기본 인자에 dict객체가 공유됨None + 내부 생성 방식 사용

0개의 댓글