좋아! 지금부터 요청한 4가지 트랩 주제를 각각 실전 문제처럼 구성해서
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. 에러 발생
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으로 초기화 후 내부에서 새로 만들어야 안전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. 순서 보장 안됨
즉, 삽입 순서대로:
d['banana'] → 첫 번째
d['apple'] → 두 번째
d['pear'] → 세 번째
| 버전 | 순서 보장 여부 |
|---|---|
| 3.5 이하 | ❌ 불보장 |
| 3.6 | ✅ CPython만 보장 (비공식) |
| 3.7 이상 | ✅ 명시적으로 보장 |
*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. 에러 발생
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는 키워드 인자 묶음 → 딕셔너리로 전달됨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. 에러 발생
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() 안에서 파일 처리 이상 동작yield)와 next 트랩원하는 거 있으면 이어서 출제해줄게!
훌륭한 선택이야! 지금부터 요청한 4가지 트랩을 하나하나 실전 문제 형식으로 내고,
각 줄마다 디버깅하듯 완전히 해부해서 설명할게.
try-except-finally에서 return 충돌 트랩def test():
try:
return "try"
except:
return "except"
finally:
return "finally"
print(test())
A. "try"
B. "except"
C. "finally"
D. 에러 발생
try:
return "try" # ✅ 실행됨
except:
return "except" # ❌ 실행되지 않음
finally:
return "finally" # ❗️ return을 **덮어씀**
finally 블록은 예외가 발생하든 말든 반드시 실행됨finally 안에 return이 있으면, 앞선 try의 return을 덮어버림finally는 무조건 실행되고return도 강제로 덮어쓴다finally에 return 쓰지 말 것with open() 안에서 파일 처리 트랩with open("data.txt", "w") as f:
f.write("hello")
print(f.closed)
A. True
B. False
C. 에러 발생
D. "hello"
with open("data.txt", "w") as f:
f.write("hello")
with 블록이 끝나면 → f.close()가 자동 호출됨f.closed는 True📌 블록 끝나는 순간 자동 정리되는 구조를 컨텍스트 매니저라고 함
with open(...) as f: → 끝나면 자동으로 닫힘f.closed → 블록 바깥에서는 항상 Trueyield) + 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. 에러 발생
def gen():
yield 1 # ✅ 첫 next() → 1
yield 2 # ✅ 두 번째 next() → 2
return 3 # ❌ 세 번째 next() → StopIteration 발생
return을 만나면 → StopIteration 예외 발생return 3은 next()에서는 출력되지 않음next(g)를 3번 호출하므로 마지막에 에러 발생yield로 값을 순차적으로 내보냄return은 끝낸다는 뜻이고, StopIteration을 일으킨다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 = 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 return | finally가 return을 덮어씀 | return은 finally에 두지 마라 |
| with open | 자동으로 닫힘 | f.closed == True |
| 제너레이터 | return → StopIteration | next()는 yield까지만 |
| 얕은 복사 | 내부 객체 공유됨 | deepcopy() 사용 |
좋아! 요청한 4가지 트랩 유형은 모두 정보처리기사 실기나 현업에서 기본기가 흔들리면 쉽게 틀리는 주제야. 각 주제별로 다음 구조로 설명할게:
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. 에러 발생
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 | 가장 가까운 바깥 함수의 변수에 접근 (지역 → 바깥 지역) |
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. 에러 발생
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)__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 ...]
print(obj) # __str__() 호출 → STR
print([obj]) # 리스트 출력은 내부 요소에 대해 __repr__() 호출 → REPR
print(obj)는 사람이 보기 좋은 문자열 → __str__print([obj])는 개발자 디버깅용 → __repr__| 메서드 | 호출되는 상황 |
|---|---|
__str__ | print(obj), str(obj) |
__repr__ | 리스트/딕셔너리 출력, repr(obj) |
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. 에러 발생
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 + 내부 생성 방식 사용 |