연습11

류한선·2025년 6월 29일

실기연습-2

목록 보기
58/95

✅ [문제] 다음 코드의 출력 결과를 구하시오

class Test:
    count = 0

    def __init__(self):
        self.items = []
        Test.count += 1

    def add(self, item):
        self.items.append(item)
        Test.count += 1

    def show(self):
        print("items:", self.items)
        print("count:", Test.count)

a = Test()
b = Test()
a.add("apple")
b.add("banana")
a.show()
b.show()

❓ 예상 출력 보기

items: ['apple']
count: 4
items: ['banana']
count: 4
items: ['apple']
count: 3
items: ['banana']
count: 3
items: ['apple']
count: 2
items: ['banana']
count: 2
items: ['apple', 'banana']
count: 4
items: ['apple', 'banana']
count: 4

✅ 정답: 1번


🔍 디버깅 & 해설

클래스 구조

  • count는 클래스 변수 → 모든 인스턴스에서 공유
  • items는 인스턴스 변수 → 각 인스턴스마다 별도

1️⃣ a = Test()

  • __init__ 실행

    • self.items = [] → a의 인스턴스 변수 초기화
    • Test.count += 1count = 1

2️⃣ b = Test()

  • __init__ 실행

    • b.items = []
    • Test.count += 1count = 2

3️⃣ a.add("apple")

  • a.items.append("apple")a.items = ['apple']
  • Test.count += 1count = 3

4️⃣ b.add("banana")

  • b.items.append("banana")b.items = ['banana']
  • Test.count += 1count = 4

5️⃣ a.show()

items: ['apple']
count: 4

6️⃣ b.show()

items: ['banana']
count: 4

📌 핵심 요약

변수위치공유 여부값 변화
count클래스 변수모든 인스턴스 공유0 → 1 → 2 → 3 → 4
items인스턴스 변수인스턴스별 개별a: ['apple'], b: ['banana']

좋아!
그럼 네가 요청한 Python의 실수하기 쉬운 4가지 주제에 대해 각각 실제 문제 + 디버깅 해설을 전부 해줄게.
(1번은 이미 위에서 해줬고, 이제 2번부터 4번까지 이어서 진행해.)


✅ [1] 클래스 변수 공유 (static 유사 동작)


🔸 문제 1 (기본): 다음 코드의 출력은?

class A:
    cnt = 0

    def __init__(self):
        A.cnt += 1

a1 = A()
a2 = A()
print(A.cnt)

👉 정답: 2


🔸 문제 2 (고급): 아래 코드의 출력은?

class A:
    values = []

    def __init__(self, x):
        self.values.append(x)

a = A(1)
b = A(2)
a.values.append(3)
print(b.values)

👉 정답: [1, 2, 3]

🧠 이유: values는 클래스 변수이므로 a, b 둘 다 같은 리스트 공유


✅ [2] 전역 변수 실수로 덮어쓰기


🔸 문제 3 (기본): 다음 코드를 실행하면 어떤 일이 발생하는가?

x = 10

def f():
    x = x + 5

f()

① 정상 실행
x는 15
global이 필요함
④ 오류 발생

👉 정답: ④ 오류 발생

🔍 해설: x = x + 5지역 변수 x를 새로 만들었다고 간주되는데, 오른쪽의 x가 초기화되지 않아서 UnboundLocalError


🔸 문제 4 (실전): 아래 코드를 수정 없이 실행했을 때 출력은?

x = 7

def foo():
    global x
    x += 3

foo()
print(x)

👉 정답: 10


✅ [3] 리스트 얕은 복사 vs 깊은 복사


🔸 문제 5 (기본): 얕은 복사를 수행하는 것은?

A. b = a[:]
B. b = copy.deepcopy(a)
C. b = copy.copy(a)
D. b = list(a)

👉 정답: A, C, D

deepcopy만 깊은 복사고 나머지는 얕은 복사야


🔸 문제 6 (중급): 아래 코드의 출력은?

import copy
a = [[0], [1]]
b = a[:]
a[0][0] = 99
print(b)

👉 정답: [[99], [1]]
👉 해설: 슬라이싱은 얕은 복사 → 내부 리스트는 참조됨


✅ 실전 연습 종합 문제

🔸 문제 9: 아래 코드의 출력은?

import copy

class C:
    shared = []

    def __init__(self, val):
        self.personal = [val]
        self.shared.append(val)

    def modify(self):
        new = copy.deepcopy(self.personal)
        new[0] *= 10
        return new

a = C(1)
b = C(2)
print(a.shared)
print(b.modify())
print(a.personal)

1️⃣ import copy

  • 역할: copy 모듈을 가져온다.
  • 왜 필요함?: copy.deepcopy()를 쓰기 위해. 얕은 복사(shallow copy)와 달리, 리스트 안쪽까지 완전히 복사함.
  • 실행 흐름: 바로 실행되고 copy 모듈이 현재 파이썬에 등록됨.

2️⃣ class C:

  • 클래스 정의 시작: C라는 사용자 정의 클래스를 만듦.
  • 이후 클래스 안의 변수와 메서드들이 정의됨.

3️⃣ shared = []

  • 클래스 변수
  • 클래스 C 자체가 가지는 변수로, 모든 인스턴스가 공유함.
  • 한 번만 만들어지고, 모든 인스턴스가 같은 리스트를 참조함.
C.shared ──► []

4️⃣ def __init__(self, val):

  • 생성자 함수. C()로 인스턴스를 만들 때 자동 호출됨.

5️⃣ self.personal = [val]

  • 인스턴스 변수 personal을 생성.
  • 리스트 형태로 만들어짐 (나중에 복사하기 위해 일부러 리스트 형태로 넣은 것)
  • 인스턴스마다 다른 값을 가짐.

6️⃣ self.shared.append(val)

  • 여기서 중요한 포인트!
  • self.shared라고 썼지만, 이건 C.shared를 참조함 (인스턴스에 shared가 없으므로 클래스 변수로 감).
  • 따라서, 클래스 전체에서 공유되는 리스트 C.sharedval을 추가함.

🧪 인스턴스 생성

🔸 a = C(1)

  • a.personal = [1]
  • C.shared = [1]
a.personal → [1]
C.shared → [1]

🔸 b = C(2)

  • b.personal = [2]
  • C.shared.append(2)C.shared = [1, 2]
b.personal → [2]
C.shared → [1, 2]

🖨 출력 분석


🔹 print(a.shared)

  • a.shared는 인스턴스에 없으므로 클래스 변수 C.shared를 참조.
  • 현재 C.shared = [1, 2]

🔚 출력:

[1, 2]

🔹 print(b.modify())

  • b.personal = [2]이므로, 그걸 복사해서 10배 만듦.
  • copy.deepcopy(b.personal)[2] 복사
  • new[0] *= 10[20]

🚨 중요:

  • newb.personal과 완전히 다른 리스트이므로, 원본은 그대로!

🔚 출력:

[20]

🔹 print(a.personal)

  • a.personal = [1]이므로 변하지 않았음. (복사한 건 b.personal)

🔚 출력:

[1]

✅ 최종 출력 결과

[1, 2]
[20]
[1]

🔎 시각화 요약

C.shared ───────────────► [1, 2]   ← 클래스 변수 (공용)

a.personal ─────────────► [1]      ← 인스턴스 a만의 리스트
b.personal ─────────────► [2]      ← 인스턴스 b만의 리스트

b.modify() → deepcopy([2]) = [20] ← 복사된 새로운 리스트 (원본과 무관)

⚠ 요점 정리

항목설명
C.shared클래스 전체에서 공유되는 리스트
self.personal인스턴스 고유 리스트
copy.deepcopy()리스트의 복사본을 만들어 원본 손상 없이 수정 가능
b.modify()b.personal을 복사해서 계산, 원본은 그대로 둠
a.shared, b.shared전부 같은 리스트 C.shared를 가리킴

✅ 정리한 문제 유형별 요약

번호주제핵심 개념
1~2클래스 변수모든 인스턴스가 공유
3~4전역 변수지역 변수 오해 주의, global 필요
5~6얕은 복사내부 객체까지 공유됨
7~8람다 캡처참조 vs 값 캡처 구분 (lambda i=i)
9종합 문제클래스 변수 + deepcopy + 인스턴스 변수 섞임

좋아! 이번엔 한 번 보면 대충 맞힐 수 있지만, 원리를 모르면 무조건 틀리는 수준의 어려운 문제를 준비했어.
이전 4가지 개념(클래스 변수, 전역 변수, 얕은/깊은 복사, 람다 캡처)을 섞거나 꼬아서 더 난이도 높은 실전 문제를 만들었어.


✅ 고난이도 실전 문제 세트


🔸 문제 1: 클래스 변수 vs 인스턴스 변수 혼용 + 리스트 얕은 참조

class Trick:
    box = []

    def __init__(self, val):
        self.box = Trick.box
        self.box.append(val)

a = Trick(1)
b = Trick(2)
b.box.append(3)

print(a.box)
print(b.box)
print(Trick.box)

✅ 정답:

[1, 2, 3]  
[1, 2, 3]  
[1, 2, 3]

🔍 해설:

  • Trick.box는 클래스 변수 → 모든 인스턴스에서 공유됨
  • self.box = Trick.box → 사실상 self.box도 같은 리스트를 참조
  • 즉, a.box, b.box, Trick.box 전부 동일한 리스트를 가리킴
    append()는 어디서 해도 전부 반영됨


🔸 문제 2: 함수 내부 전역 변수 + 얕은 복사 트릭

x = [10, 20]

def f():
    global x
    y = x
    x = x[:]
    x[0] += 100
    y[1] += 100

f()
print(x)

✅ 정답:

[110, 20]

🔍 디버깅 해설

  1. y = x → y와 x는 같은 리스트 [10, 20] 가리킴
  2. x = x[:] → 이제 x는 새로운 리스트 [10, 20] (얕은 복사!)
  3. x[0] += 100 → x는 [110, 20]
  4. y[1] += 100 → y는 원래의 [10, 20][10, 120]
  5. global xx는 새로운 리스트로 바뀜
    → 결과는 x = [110, 20] (y에서 바뀐 건 반영되지 않음) y[1] += 100 → y는 원래 x를 참조하고 있었으므로 x = x[:] 하기 전의 리스트를 참조
    → 그래서 y[1] = 20 → 120
    → 하지만 이후의 x는 슬라이싱한 새 리스트
    → 즉, y와 x는 다른 리스트를 가리킴.


🔸 문제 3: lambda와 클래스 변수 섞기

class F:
    funcs = []

    def __init__(self, x):
        F.funcs.append(lambda: x)

F(10)
F(20)
for f in F.funcs:
    print(f())

✅ 정답:

10  
20

🔍 해설

  • lambda: x__init__() 안에서 정의됨
  • 이때 x지역 변수이자 정수 값이라 immutable
  • lambda는 그 시점의 x값으로 캡처

따라서 각각 10, 20이 그대로 유지됨
(만약 for문 안에서 lambda 만들었으면 20, 20 나왔을 수도 있음!)



✅ 보너스 퀴즈 (개념 이해용 단답)

다음 중 출력 결과가 [1, 2, 3]이 아닌 것은?

A.

x = []
def add():
    x.append(1)
add(); x.append(2); add()
print(x)

B.

def add(x=[]):
    x.append(1)
    return x
print(add())
print(add())
print(add())

C.

def add(x=None):
    if x is None:
        x = []
    x.append(1)
    return x
print(add())
print(add())
print(add())

👉 정답: C

  • C는 x=None 패턴으로 호출 시마다 새로운 리스트 생성됨
  • A, B는 기존 리스트에 누적

0개의 댓글