[강의 요약]
[Ch 03. 파이썬 중급] 강의 수강
21_얕은복사와 깊은 복사부터 24_생성자(02)까지 강의 수강하였음
🐢 100일 챌린지 🔥 : [■■■■■■■■ ] 16/100일 (16%)
할 게 너무 많은데 머리가 따라가질 못하는 느낌이라 아쉽다.
조금 더 효율적으로 공부할 수 있는 방법을 시도해 봐야겠다.
지쳐가는 것 같은데 아직 100일까지 한참 남았다! 이런
[얕은복사와 깊은복사]
- 얕은 복사
- 얕은 복사는 '객체 주소만 복사'하는 방식
- 변수끼리 연결만 되어 있고, 실제 데이터는 하나만 존재함
- 하나를 수정하면 다른 것도 함께 바뀐다.
class TemCls:
def __init__(self, n, s):
self.number = n
self.str = s
def printClsInfo(self):
print(f'self.number: {self.number}')
print(f'self.str: {self.str}')
# 얕은 복사
tc1 = TemCls(10, 'Hello')
tc2 = tc1 # 주소만 복사됨
tc2.number = 3.14
tc2.str = 'Bye'
tc1.printClsInfo() # 변경된 값 출력됨
tc2.printClsInfo()
★ 출력 결과 ★
둘 다 출력하면 3.14와 'Bye'가 나옴 → 같은 객체를 가리키고 있음
- 깊은 복사
- 깊은 복사는 '객체를 통째로 복제'하는 방식
- 두 개의 완전히 독립적인 객체가 생김
- 하나를 바꿔도 다른 객체에 영향 없음
import copy
tc1 = TemCls(10, 'Hello')
tc2 = copy.copy(tc1) # 깊은 복사
tc2.number = 3.14
tc2.str = 'Bye'
tc1.printClsInfo() # 여전히 10, Hello
tc2.printClsInfo() # 3.14, Bye
★ 출력 결과 ★
각자 다른 값을 유지 → 복사된 진짜 '새로운 객체'
- 리스트에서의 복사 차이
scores = [9, 8, 5]
scoresCopy = scores # 얕은 복사
scoresCopy.append(10)
print(scores) # [9, 8, 5, 10]
print(scoresCopy) # [9, 8, 5, 10]
★ 출력 결과 ★
둘 다 바뀜 → 주소만 복사했기 때문
# 깊은 복사 방식
scoresCopy = scores.copy()
# 또는
scoresCopy = scores[:]
★ 출력 결과 ★
복사 후 각자 다르게 수정 가능함!
▶ 요약
[클래스 상속]
프로그래밍에서는 비슷한 기능을 가진 코드를 반복해서 만들기보다
한 번 만든 기능을 다른 클래스에서 "물려받아" 사용하는 방식을 자주 사용함
이걸 뭐라고 한다? "상속"이라고 한다.
- 상속이란?
- 클래스는 또 다른 클래스를 상속해서 마치 자기 것처럼 기능을 사용할 수 있다.
- 부모 클래스에 있는 기능을 자식 클래스가 그대로 쓸 수 있고, 필요하면 추가하거나 바꿀 수 있다.
- 상속의 구조
- Class2는 Class1을 상속받아 그 안의 기능을 그대로 사용 가능
- Class3은 Class2를 상속받아 Class1과 Class2의 기능을 모두 사용 가능
Class1 ← Class2 ← Class3
상속은 한 번만 가능한 것이 아니라 연쇄적으로도 사용 가능함(예시처럼)
- 예시 : 자동차 클래스
class NormalCar:
def drive(self):
print('[NormalCar] drive() called!!')
def back(self):
print('[NormalCar] back() called!!')
# TurboCar는 NormalCar를 상속받음
class TurboCar(NormalCar):
def turbo(self):
print('[TurboCar] turbo() called!!')
myCar = TurboCar()
myCar.drive() # NormalCar의 메서드 사용
myCar.back() # NormalCar의 메서드 사용
myCar.turbo() # TurboCar의 고유 메서드
★ 출력 결과 ★
[NormalCar] drive() called!!
[NormalCar] back() called!!
[TurboCar] turbo() called!!
TurboCar는 NormalCar의 기능을 물려받았기 때문에, drive()와 back()도 쓸 수 있음
- 예시 : 자동차 클래스
class CalculatorSuper:
def add(self, n1, n2):
return n1 + n2
def sub(self, n1, n2):
return n1 - n2
class CalculatorChild(CalculatorSuper):
def mul(self, n1, n2):
return n1 * n2
def div(self, n1, n2):
return n1 / n2
cal = CalculatorChild()
print(cal.add(10, 20)) # 부모 클래스 기능
print(cal.sub(10, 20)) # 부모 클래스 기능
print(cal.mul(10, 20)) # 자식 클래스 기능
print(cal.div(10, 20)) # 자식 클래스 기능
CalculatorChild는 CalculatorSuper를 상속받았기 때문에
부모의 add, sub 기능을 그대로 사용할 수 있다.
그리고 자식 클래스에서는 mul, div 기능을 새로 추가해 기능을 확장했음
[생성자]
- 생성자란?
- 객체가 생성될 때 자동으로 실행되는 특별한 함수가 바로 생성자
- 파이썬에서는 __init__() 메서드가 생성자 역할을 하며, 객체가 만들어질 때 자동으로 호출됨
- 기본 생성자 사용
- cal = Calculator() 객체를 생성하면 __init__()이 자동으로 실행
class Calculator:
def __init__(self):
print('[Calculator] __init__() called!!')
cal = Calculator()
★ 출력 결과 ★
[Calculator] __init__() called!!
- 생성자에서 속성 초기화하기
- self.num1과 self.num2는 객체의 속성
- 객체를 생성하면서 10, 20을 넘기면 각각 속성으로 저장됨
class Calculator:
def __init__(self, n1, n2):
print('[Calculator] __init__() called!!')
self.num1 = n1
self.num2 = n2
cal = Calculator(10, 20)
print(f'cal.num1: {cal.num1}')
print(f'cal.num2: {cal.num2}')
★ 출력 결과 ★
[Calculator] __init__() called!!
cal.num1: 10
cal.num2: 20
- 상속에서의 생성자와 super()
- C_Class는 P_Class를 상속한 자식 클래스
- super().__init__(100, 200)는 부모 클래스의 생성자를 호출해 상속받은 속성도 초기화
class P_Class:
def __init__(self, pNum1, pNum2):
print('[pClass] __init__()')
self.pNum1 = pNum1
self.pNum2 = pNum2
class C_Class(P_Class):
def __init__(self, cNum1, cNum2):
print('[cClass] __init__()')
super().__init__(100, 200) # 부모 클래스 초기화
self.cNum1 = cNum1
self.cNum2 = cNum2
cls = C_Class(10, 20)
★ 출력 결과 ★
[cClass] __init__()
[pClass] __init__()
cls.cNum1: 10
cls.cNum2: 20
cls.pNum1: 100
cls.pNum2: 200
- 실습 코드
- 중간고사 클래스와 기말고사 클래스를 상속 관계로 만들고 각각의 점수를 초기화
- 또한 총점 및 평균을 반환하는 기능도 만들어야 함
- MidExam 클래스 정의
- 생성자 __init__()는 중간고사 점수 3개를 객체 속성으로 저장
- printScores()는 점수를 출력하는 메서드
class MidExam:
def __init__(self, s1, s2, s3):
print('[MidExam] __init__()')
self.mid_kor_score = s1
self.mid_eng_score = s2
self.mid_mat_score = s3
def printScores(self):
print(f'mid_kor_score: {self.mid_kor_score}')
print(f'mid_eng_score: {self.mid_eng_score}')
print(f'mid_mat_score: {self.mid_mat_score}')
- EndExam 클래스 정의 (MidExam 상속)
- super().__init__()를 통해 부모 클래스의 점수 초기화도 함께 처리
- 추가로 기말고사 점수도 객체에 저장
class EndExam(MidExam):
def __init__(self, s1, s2, s3, s4, s5, s6):
print('[EndExam] __init__()')
super().__init__(s1, s2, s3) # 부모 클래스 초기화
self.end_kor_score = s4
self.end_eng_score = s5
self.end_mat_score = s6
- 점수 출력 메서드
- 부모 클래스의 printScores()도 호출하고,
- 자신만의 출력도 추가(메서드 오버라이딩)
def printScores(self):
super().printScores() # 중간고사 점수 먼저 출력
print(f'end_kor_score: {self.end_kor_score}')
print(f'end_eng_score: {self.end_eng_score}')
print(f'end_mat_score: {self.end_mat_score}')
- 총점 및 평균 계산
- 총 6개의 과목 점수를 더하고 평균을 계산
def getTotalScore(self):
total = self.mid_kor_score + self.mid_eng_score + self.mid_mat_score
total += (self.end_kor_score + self.end_eng_score + self.end_mat_score)
return total
def getAverageScore(self):
return self.getTotalScore() / 6
- 실행
exam = EndExam(85, 90, 88, 75, 85, 95)
exam.printScores()
print(f'Total: {exam.getTotalScore()}')
print(f'Average: {round(exam.getAverageScore(), 2)}')
★ 출력 결과 ★
[EndExam] __init__()
[MidExam] __init__()
mid_kor_score: 85
mid_eng_score: 90
mid_mat_score: 88
end_kor_score: 75
end_eng_score: 85
end_mat_score: 95
Total: 518
Average: 86.33
▶ 요약
[나의 생각 정리]
- 클래스를 상속받아 코드를 효율적으로 구성하는 법을 배웠다.
- super()을 통해 부모 클래스의 기능을 재사용하는 것이 유용하다는 것을 알았다.
[적용점]
- 클래스 설계 시 중복을 줄이기 위해 상속과 super() 활용을 하면 좋을 것 같다.
“이 글은 제로베이스 데이터 스쿨 주 3일반 강의 자료 일부를 발췌하여 작성되었습니다.”
'제로베이스 데이터 취업 파트타임 > 100일 챌린지_일일 학습 일지' 카테고리의 다른 글
제로베이스 데이터 파트타임 스쿨 학습 일지 [25.04.03] (0) | 2025.04.03 |
---|---|
제로베이스 데이터 파트타임 스쿨 학습 일지 [25.04.02] (1) | 2025.04.02 |
제로베이스 데이터 파트타임 스쿨 학습 일지 [25.03.31] (0) | 2025.03.31 |
제로베이스 데이터 파트타임 스쿨 학습 일지 [25.03.30] (0) | 2025.03.30 |
제로베이스 데이터 파트타임 스쿨 학습 일지 [25.03.29] (0) | 2025.03.29 |