해시 연습 3(x+y 조심)

류한선·2025년 7월 3일

실기연습-2

목록 보기
77/95

좋아요! 정보처리기사 실기에서는 기본 문법이나 자주 쓰이는 기능헷갈리게 꼬아서 출제하는 문제가 많습니다. 예를 들어 변수의 범위(scope), 참조/복사 차이, 조건문의 흐름, 반복문의 탈출 조건 등 기본 문법이지만 조금만 꼬면 틀리게 되는 것들이죠.


🧠 오늘 연습할 유형: 기본기 함정 문제 – 변수의 스코프와 리스트 참조

✅ 문제:

다음 파이썬 코드를 실행했을 때 출력 결과는 무엇일까요?

def modify_list(lst):
    lst.append(4)
    lst = [1, 2, 3]
    lst.append(5)

my_list = [0]
modify_list(my_list)
print(my_list)

❓ 보기:

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


🔍 풀이 및 해설

이 코드는 리스트를 함수에 인자로 넘길 때 참조/복사 관계와 **지역 변수의 스코프(scope)**를 묻는 문제입니다.


▶️ 코드 분석

def modify_list(lst):
    lst.append(4)     # (1)
    lst = [1, 2, 3]    # (2)
    lst.append(5)      # (3)

(1) lst.append(4)

  • lst는 함수 호출 시 my_list를 그대로 참조하고 있음
  • 즉, my_list = [0]lst.append(4)를 하면 my_list = [0, 4]가 됨
  • 이때까지는 my_list와 lst가 같은 객체

(2) lst = [1, 2, 3]

  • 여기서 lst새로운 리스트 [1, 2, 3]을 가리키도록 다시 할당
  • 중요한 포인트: 이제 lst는 더 이상 my_list를 참조하지 않음
  • my_listlst는 갈라짐

(3) lst.append(5)

  • lst[1, 2, 3]이므로 → [1, 2, 3, 5]가 되지만
  • 이건 my_list에는 아무런 영향 없음

my_list = [0]
modify_list(my_list)
print(my_list)
  • modify_list 함수 안에서 my_list[0][0, 4]로 변경됨
  • 그 뒤에 새로운 리스트를 만들어서 조작한 건 my_list와 관계없음

✅ 최종 정답: B. [0, 4]


📌 요약 포인트:

  • 리스트는 참조로 전달되지만, 새로운 리스트를 할당하면 분리됨
  • 함수 내에서 기존 리스트를 조작하면 영향이 있고, 새로 할당하면 영향이 없음
  • 이 문제는 “변수는 어디까지 영향을 미치는가?”를 묻는 대표적인 스코프/참조 트랩 문제입니다

좋아! 이번엔 네가 요청한 딕셔너리, set, 객체 관련해서 정보처리기사 실기에서 헷갈리도록 꼬는 유형으로 문제를 3개 낼게. 각 문제는 "틀릴 수밖에 없는 기본기 트랩"을 주제로 구성했고, 각 문제 뒤에 풀이완전 해부 해설을 붙였어.


🧩 문제 1: 딕셔너리 키 충돌 트랩

class Item:
    def __init__(self, name):
        self.name = name

item1 = Item("apple")
item2 = Item("apple")

d = {}
d[item1] = 100
d[item2] = 200

print(len(d))

보기:

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


✅ 정답: B. 2


🔍 해설

⚠️ 여기서 트랩: item1item2name이 같아도 다른 객체

  • 파이썬 딕셔너리는 내부적으로 __hash__()__eq__()를 기준으로 키를 비교함
  • 위 클래스 Item__hash__()__eq__()오버라이드하지 않았기 때문에,
    두 객체는 다른 해시값을 가지며 다른 키로 인식됨

즉:

id(item1) != id(item2)
hash(item1) != hash(item2)

그래서 d에는 서로 다른 두 키가 들어가므로 len(d) = 2


💡 요약:

  • 딕셔너리에서 객체를 키로 쓰려면 **__hash__, __eq__**를 반드시 오버라이드해야 키 충돌을 제대로 처리함!

🧩 문제 2: set과 가변 객체 트랩

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

s = set()
s.add(tuple(a))
s.add(tuple(b))

print(len(s))

보기:

A. 1
B. 2
C. TypeError
D. None


✅ 정답: A. 1


🔍 해설

  • ab는 리스트지만, 리스트는 set에 넣을 수 없음 → 가변(mutable) 타입이기 때문
  • 그래서 tuple()로 바꿔서 넣음 → tuple(a) = (1, 2)

하지만 tuple(a)tuple(b)내용이 같고, 불변형이라 해시값도 같음

tuple(a) == tuple(b)True
hash(tuple(a)) == hash(tuple(b))True

그래서 set 안에선 중복으로 판단 → 한 개만 저장됨


💡 요약:

  • set중복 제거 + 해시 가능한(불변) 자료형만 저장 가능
  • 같은 값을 가진 튜플은 하나만 남는다

🧩 문제 3: 객체와 속성 공유 트랩 (기본값 공유 문제)

class Person:
    def __init__(self, hobbies=[]):
        self.hobbies = hobbies

p1 = Person()
p2 = Person()

p1.hobbies.append("coding")
print(p2.hobbies)

보기:

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


✅ 정답: B. ['coding']


🔍 해설

⚠️ 여기서 트랩: 함수의 기본 인자로 가변 객체를 쓰면 인스턴스 간에 공유됨!

  • __init__(self, hobbies=[]) 는 한 번 생성된 [] 리스트 객체를 여러 인스턴스에서 공유
  • 그래서 p1.hobbies.append("coding") 하면 p2.hobbies도 똑같은 리스트 참조 중
p1.hobbies is p2.hobbies → True

🔧 해결법

기본 인자로 None을 주고, 내부에서 새 리스트를 만드는 방식이 안전함:

class Person:
    def __init__(self, hobbies=None):
        if hobbies is None:
            self.hobbies = []
        else:
            self.hobbies = hobbies

💡 요약:

  • 함수 기본 인자로 [], {} 등 가변 객체 쓰면 함정
  • 객체끼리 공유되면서 예기치 않은 결과 발생

좋아, 지금부터 요청한 3개 주제 —

  1. 함수의 인자 전달 (값 vs 참조) 트랩
  2. for문 안의 lambda / 클로저 트랩
  3. 리스트 컴프리헨션 안의 변수 스코프 트랩

각각 대표적인 트랩 문제 하나씩 출제하고, 초보자도 완벽하게 이해할 수 있도록 각 줄 해설 + 값 추적 + 핵심 요약까지 해줄게.


🔍 1. 함수의 인자 전달 트랩 (값 vs 참조)

def update(x, y):
    x += y
    y += x

a = [1, 2]
b = [3, 4]
update(a, b)
print(a)

보기:

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


✅ 정답: A. [1, 2, 3, 4]


🔍 해설 한 줄씩:

def update(x, y):
    x += y         # x = x + y → 리스트이므로 x.extend(y)
    y += x         # y = y + x → 새 리스트 만들어서 y에 할당 (지역 변수 y)
  • 여기서 x += y리스트의 in-place 연산 (수정됨)
  • 하지만 y += x는 지역변수 y만 바뀌고, 외부 변수 b는 영향 X
# 처음 상태:
a = [1, 2]
b = [3, 4]

# update(a, b) 호출 후:
a → [1, 2, 3, 4]
b → 변함 없음

💡 요약:

  • 리스트 x += y참조된 원본을 수정
  • 하지만 y += x새 객체가 y에 할당될 뿐, 원본에는 영향 X

🔍 2. for문 + lambda 트랩 (클로저 변수 고정 문제)

funcs = []
for i in range(3):
    funcs.append(lambda: i)

results = [f() for f in funcs]
print(results)

보기:

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


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


🔍 해설:

for i in range(3):
    funcs.append(lambda: i)
  • 여기서 lambda: i는 함수를 만드는 코드인데
  • i의 값은 "지금"이 아니라, 호출될 "그때" 기준으로 평가됨
  • 그래서 모든 람다가 i공유하고 있고, 마지막엔 i = 2
# funcs = [<lambda: i>, <lambda: i>, <lambda: i>]
# 나중에 실행되면 모두 i == 2

✔️ 해결 방법 (변수 캡처하기):

for i in range(3):
    funcs.append(lambda i=i: i)
  • 이렇게 하면 i=i기록 시점의 값을 고정시킬 수 있음

💡 요약:

  • lambda변수를 캡처하지만 값은 캡처하지 않음
  • 그래서 루프 후에 실행하면 모두 마지막 값만 기억

🔍 3. 리스트 컴프리헨션 스코프 트랩

x = 'global'

lst = [x for x in range(3)]

print(x)

보기:

A. 'global'
B. 2
C. 3
D. 에러 발생


✅ 정답: A. 'global'`


🔍 해설:

✅ 파이썬 3에서 리스트 컴프리헨션은 자체 스코프를 가짐

x = 'global'               # 전역 x = 'global'
lst = [x for x in range(3)]  # 여기 x는 리스트 컴프리헨션 내부의 지역 변수
  • 파이썬 3.x에서는 리스트 컴프리헨션이 함수처럼 자체 스코프를 가짐
  • 즉, 그 안의 x는 외부 x와는 완전히 다른 변수
  • 전역 변수 x는 변경되지 않음

💡 요약:

  • 리스트 컴프리헨션은 파이썬 3부터 자체 스코프를 갖기 때문에
  • 내부 변수는 외부에 영향 주지 않음

✅ 정리 요약

트랩 유형핵심 함정기억 포인트
함수 인자 (값 vs 참조)+= 같은 연산의 행동이 자료형마다 다름리스트는 in-place 변경됨
for + lambda 클로저루프 변수는 나중에 실행할 때 참조됨lambda i=i로 고정
리스트 컴프리헨션 스코프컴프리헨션 내부 변수는 외부 변수에 영향 안 줌파이썬 3부터는 자체 스코프

0개의 댓글