
import math
print(math.sqrt(16)) # 출력: 4.0 (제곱근) 제곱근 반환 (√x)
print(math.pow(2, 3)) # 출력: 8.0 (거듭제곱) 거듭제곱 (x^y)
print(math.factorial(5)) # 출력: 120 (5!) 팩토리얼 계산 (n!)
print(math.gcd(48, 18)) # 출력: 6 (최대공약수) 최대공약수 (GCD)
print(math.floor(3.9)) # 출력: 3 (내림) 내림 (소수점 이하 버림)
print(math.ceil(3.1)) # 출력: 4 (올림) 올림 (소수점 이하 올림)
print(math.pi) # 출력: 3.141592653589793 원주율 π 값
print(math.e) # 출력: 2.718281828459045 자연로그 e 값
import random
print(random.random()) # 0.0 ~ 1.0 사이 난수
print(random.randint(1, 10)) # 1~10 사이 정수 난수
print(random.uniform(1.5, 5.5)) # 1.5~5.5 사이 실수 난수
lst = [1, 2, 3, 4, 5]
print(random.choice(lst)) # 리스트에서 랜덤 선택
print(random.choices(lst, k=3)) # 중복 가능하게 3개 선택
print(random.sample(lst, 2)) # 중복 없이 2개 선택
random.shuffle(lst) # 리스트 요소 섞기
print(lst)
import time
print(time.time()) # 현재 시간(Unix timestamp)
time.sleep(2) # 2초 동안 실행 멈춤
print("2초 후 실행됨")
current_time = time.localtime()
print(time.strftime("%Y-%m-%d %H:%M:%S", current_time)) # 현재 시간을 YYYY-MM-DD HH:MM:SS 형태로 출력
1️⃣. 객체(Object)란?
객체는 속성(데이터)과 기능(메서드, 동작)을 포함하는 독립적인 단위다.
예를 들어 자동차 객체를 생각해보자.
🚗 자동차 객체의 예시
• 속성(Attributes, 변수)
• 색상: 빨강
• 브랜드: 현대
• 속도: 60km/h
기능(Methods, 함수)
• 가속(): 속도 증가
• 감속(): 속도 감소
• 멈춤(): 정지
class Car:
def __init__(self, color, brand, speed=0):
self.color = color # 속성
self.brand = brand # 속성
self.speed = speed # 속성
def accelerate(self): # 기능(메서드)
self.speed += 10
print(f"{self.brand} 자동차 속도 증가: {self.speed}km/h")
def brake(self): # 기능(메서드)
self.speed -= 10
print(f"{self.brand} 자동차 속도 감소: {self.speed}km/h")
# 객체 생성
my_car = Car("빨강", "현대")
# 기능 실행
my_car.accelerate() # 출력: 현대 자동차 속도 증가: 10km/h
my_car.brake() # 출력: 현대 자동차 속도 감소: 0km/h
2️⃣. 객체지향 프로그래밍의 4대 특징
✅ 객체(Object) = 속성(데이터) + 기능(메서드)
✅ 캡슐화: 데이터를 숨기고 메서드를 통해 접근
✅ 상속: 부모 클래스의 기능을 자식 클래스가 재사용
✅ 다형성: 같은 메서드 이름이지만 클래스마다 다르게 동작
✅ 추상화: 필요한 기능만 노출하고, 세부 사항은 숨김
1) 캡슐화 (Encapsulation)
• 속성과 메서드를 하나의 객체로 묶고, 외부에서 직접 접근을 제한하는 것.
• 보통 속성을 private(비공개) 속성으로 만들고, 메서드를 통해 접근한다.
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner # 공개 속성
self.__balance = balance # 비공개 속성 (외부에서 접근 불가)
def deposit(self, amount):
self.__balance += amount
print(f"{self.owner}님, {amount}원이 입금되었습니다.")
def get_balance(self):
return self.__balance
# 객체 생성
account = BankAccount("홍길동", 1000)
# balance 속성은 직접 접근 불가
# print(account.__balance) # 오류 발생
# 대신 메서드를 통해 접근
print(account.get_balance()) # 출력: 1000
account.deposit(500)
print(account.get_balance()) # 출력: 1500
2) 상속 (Inheritance)
• 기존 클래스를 재사용하여 새로운 클래스를 만드는 것.
• **부모 클래스(기본 클래스)**의 속성과 메서드를 **자식 클래스(파생 클래스)**가 상속받아 사용할 수 있다.
# 부모 클래스
class Animal:
def __init__(self, name):
self.name = name
def make_sound(self):
print("소리를 낸다.")
# 자식 클래스
class Dog(Animal):
def make_sound(self):
print(f"{self.name}가 멍멍 짖는다.")
# 객체 생성
dog = Dog("바둑이")
dog.make_sound() # 출력: 바둑이가 멍멍 짖는다.
3) 다형성 (Polymorphism)
• 같은 메서드 이름을 사용하지만, 각 클래스에서 다르게 동작하도록 하는 것.
class Bird:
def fly(self):
print("새가 난다.")
class Airplane:
def fly(self):
print("비행기가 난다.")
# 동일한 fly() 메서드를 호출하지만, 각 객체마다 다르게 동작
bird = Bird()
airplane = Airplane()
bird.fly() # 출력: 새가 난다.
airplane.fly() # 출력: 비행기가 난다.
4) 추상화 (Abstraction)
• 불필요한 부분은 숨기고, 필요한 부분만 보여주는 것.
• 추상 클래스를 사용하여 공통적인 기능을 정의하고, 구체적인 구현은 자식 클래스에서 하도록 강제한다.
from abc import ABC, abstractmethod
# 추상 클래스
class Animal(ABC):
@abstractmethod
def make_sound(self):
pass # 자식 클래스에서 반드시 구현해야 함
# 자식 클래스
class Cat(Animal):
def make_sound(self):
print("야옹!")
class Dog(Animal):
def make_sound(self):
print("멍멍!")
# 객체 생성
cat = Cat()
dog = Dog()
cat.make_sound() # 출력: 야옹!
dog.make_sound() # 출력: 멍멍!
객체지향 프로그래밍(OOP)에서 클래스(Class)는 객체를 생성하기 위한 설계도(청사진)이고, 객체(Object)는 그 설계도를 기반으로 만들어진 실제 인스턴스(Instance)이다.
클래스 정의 방법
class 클래스이름:
# 생성자 (객체 생성 시 실행되는 특수한 메서드)
def __init__(self, 속성1, 속성2):
self.속성1 = 속성1
self.속성2 = 속성2
# 메서드 정의
def 메서드이름(self):
print("이것은 메서드입니다.")
# 클래스 정의
class Person:
def __init__(self, name, age): # 생성자 (객체 생성 시 실행)
self.name = name # 속성 (name)
self.age = age # 속성 (age)
def introduce(self): # 메서드
print(f"안녕하세요, 제 이름은 {self.name}이고, 나이는 {self.age}살입니다.")
# 객체 생성
person1 = Person("홍길동", 25)
person2 = Person("김철수", 30)
# 메서드 호출
person1.introduce() # 출력: 안녕하세요, 제 이름은 홍길동이고, 나이는 25살입니다.
person2.introduce() # 출력: 안녕하세요, 제 이름은 김철수이고, 나이는 30살입니다.
class Car:
def __init__(self, brand, color):
self.brand = brand
self.color = color
car1 = Car("현대", "빨강") # 객체 생성 (자동으로 __init__ 실행)
print(car1.brand) # 출력: 현대
print(car1.color) # 출력: 빨강
🔹 인스턴스 변수
- self.변수명 형태로 선언된 변수.
- 각 객체(인스턴스)마다 개별적으로 저장된다.
class Dog:
def __init__(self, name):
self.name = name # 인스턴스 변수
dog1 = Dog("바둑이")
dog2 = Dog("초코")
print(dog1.name) # 출력: 바둑이
print(dog2.name) # 출력: 초코
🔹 클래스 변수
- 클래스이름.변수명 형태로 선언된 변수.
- 클래스에 속한 모든 객체가 공유한다.
class Animal:
species = "포유류" # 클래스 변수
dog = Animal()
cat = Animal()
print(dog.species) # 출력: 포유류
print(cat.species) # 출력: 포유류
class Calculator:
def add(self, a, b):
return a + b
def subtract(self, a, b):
return a - b
calc = Calculator() # 객체 생성
print(calc.add(10, 5)) # 출력: 15
print(calc.subtract(10, 5)) # 출력: 5
🔹 클래스 메서드 (@classmethod)
class Animal:
species = "포유류" # 클래스 변수
@classmethod
def set_species(cls, new_species):
cls.species = new_species
Animal.set_species("파충류")
print(Animal.species) # 출력: 파충류
🔹 정적 메서드 (@staticmethod)
class MathUtils:
@staticmethod
def add(a, b):
return a + b
print(MathUtils.add(3, 7)) # 출력: 10
📌 정리
✅ 클래스: 객체를 생성하는 설계도
✅ 객체: 클래스를 기반으로 만들어진 실제 인스턴스
✅ 생성자 (init): 객체가 생성될 때 실행되는 초기화 함수
✅ 인스턴스 변수: 각 객체별로 개별 저장
✅ 클래스 변수: 모든 객체가 공유
✅ 메서드: 클래스 내부에서 정의된 함수
✅ 클래스 메서드 (@classmethod): 클래스 자체를 조작할 때 사용
✅ 정적 메서드 (@staticmethod): 독립적인 함수 역할 수행
객체를 생성한 후, 속성을 변경하는 방법과 객체가 메모리에 어떻게 관리되는지
🔹 (1) 직접 속성 변경
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# 객체 생성
p1 = Person("홍길동", 25)
# 속성 변경
p1.age = 30
print(p1.age) # 출력: 30
🔹 (2) 메서드를 통한 속성 변경
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def set_age(self, new_age): # 나이 변경 메서드
if new_age > 0:
self.age = new_age
else:
print("나이는 0보다 커야 합니다.")
# 객체 생성
p1 = Person("김철수", 20)
# 메서드를 통한 속성 변경
p1.set_age(35)
print(p1.age) # 출력: 35
p1.set_age(-5) # 출력: 나이는 0보다 커야 합니다.
🔹 (3) setattr() 함수로 속성 변경
class Car:
def __init__(self, brand, color):
self.brand = brand
self.color = color
# 객체 생성
car = Car("현대", "빨강")
# setattr()을 이용한 속성 변경
setattr(car, "color", "파랑")
print(car.color) # 출력: 파랑
🔹 (4) dict로 속성 변경
class Animal:
def __init__(self, name, species):
self.name = name
self.species = species
dog = Animal("바둑이", "강아지")
# 객체의 속성 출력
print(dog.__dict__) # 출력: {'name': '바둑이', 'species': '강아지'}
# 속성 변경
dog.__dict__["name"] = "초코"
print(dog.name) # 출력: 초코
객체와 메모리 관리 :
파이썬에서는 객체가 메모리에 어떻게 관리되는지 이해하는 것이 중요하다.
🔹 (1) 객체의 메모리 할당
class Person:
def __init__(self, name):
self.name = name
p1 = Person("홍길동") # p1은 '홍길동' 객체의 참조를 저장함
📌 메모리 구조
🔹 (2) 여러 변수가 같은 객체를 참조할 경우
a = [1, 2, 3]
b = a # b는 a와 같은 객체를 참조함
b.append(4)
print(a) # 출력: [1, 2, 3, 4]
print(b) # 출력: [1, 2, 3, 4]
📌 a와 b는 같은 메모리 주소를 참조하므로 b를 변경하면 a도 변경됨.
✅ 확인 방법 (id())
객체의 메모리 주소(참조 ID)를 확인하려면 id() 함수를 사용하면 된다.
print(id(a)) # a의 메모리 주소 출력
print(id(b)) # b의 메모리 주소 출력 (a와 동일)
🔹 (3) 얕은 복사 vs 깊은 복사
import copy
lst1 = [1, 2, [3, 4]]
lst2 = copy.copy(lst1) # 얕은 복사
lst2[2].append(5)
print(lst1) # 출력: [1, 2, [3, 4, 5]]
print(lst2) # 출력: [1, 2, [3, 4, 5]]
lst1 = [1, 2, [3, 4]]
lst2 = copy.deepcopy(lst1) # 깊은 복사
lst2[2].append(5)
print(lst1) # 출력: [1, 2, [3, 4]]
print(lst2) # 출력: [1, 2, [3, 4, 5]]
Immutable과 Mutable
🔹 (4) 가비지 컬렉션 (Garbage Collection)
import gc
class Test:
def __del__(self):
print("객체가 메모리에서 삭제됨")
obj = Test() # 객체 생성
del obj # 객체 삭제 (가비지 컬렉터가 실행되면 __del__이 호출됨)
gc.collect() # 가비지 컬렉터 수동 실행
• 객체.속성 = 값 방식으로 직접 변경
• setattr(객체, "속성", 값)으로 변경
• __dict__를 활용한 속성 변경
• 객체는 힙(Heap) 메모리에 저장되고, 변수가 이를 참조
• id(객체)를 사용하면 객체의 메모리 주소 확인 가능
• copy.copy()(얕은 복사)와 copy.deepcopy()(깊은 복사) 차이 이해 필요
• 참조 카운트가 0이 되면 가비지 컬렉션(GC)에 의해 자동 삭제됨
클래스 상속은 기존 클래스를 재사용하여 새로운 클래스를 만드는 기법이다.
부모 클래스(기본 클래스)의 속성(변수)과 메서드(함수)를 자식 클래스(파생 클래스)가 물려받아 사용할 수 있다.
Class 1
🔹 기본적인 상속
# 부모 클래스 정의
class Parent:
def parent_method(self):
print("부모 클래스 메서드")
# 자식 클래스 정의 (Parent를 상속)
class Child(Parent):
def child_method(self):
print("자식 클래스 메서드")
# 객체 생성
child = Child()
child.parent_method() # 부모 클래스 메서드 호출 가능
child.child_method() # 자식 클래스 메서드 호출
✅ Child 클래스는 Parent 클래스를 상속받았으므로, parent_method()를 직접 사용할 수 있다.
부모 클래스의__ init __()을 자식 클래스에서 재사용하거나, 확장할 수 있다.
🔹 부모의 생성자 사용하기 (super())
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def introduce(self):
print(f"이름: {self.name}, 나이: {self.age}")
class Student(Person): # Person 클래스를 상속
def __init__(self, name, age, student_id):
super().__init__(name, age) # 부모 클래스의 __init__() 호출
self.student_id = student_id # 자식 클래스만의 속성 추가
def introduce(self):
super().introduce() # 부모 클래스의 introduce() 호출
print(f"학번: {self.student_id}")
# 객체 생성
s1 = Student("김철수", 20, "2024001")
s1.introduce()
이름: 김철수, 나이: 20
학번: 2024001
✅ super().__ init __(...)을 호출하여 부모 클래스의 생성자를 실행한 후, 추가적인 속성을 설정한다.
class Animal:
def make_sound(self):
print("동물이 소리를 냅니다.")
class Dog(Animal):
def make_sound(self): # 부모 메서드 재정의 (오버라이딩)
print("멍멍!")
# 객체 생성
dog = Dog()
dog.make_sound() # 출력: 멍멍!
✅ Dog 클래스에서 make_sound()를 재정의했기 때문에, 부모의 메서드가 아니라 자식 클래스의 메서드가 실행된다.
🔹 부모의 메서드도 호출하고 싶다면?
class Dog(Animal):
def make_sound(self):
super().make_sound() # 부모 메서드 호출
print("멍멍!")
dog = Dog()
dog.make_sound()
📌 실행 결과
동물이 소리를 냅니다.
멍멍!
파이썬에서는 하나의 클래스가 여러 개의 부모 클래스를 상속받을 수 있다.
class Parent1:
def method1(self):
print("Parent1의 메서드")
class Parent2:
def method2(self):
print("Parent2의 메서드")
# 다중 상속
class Child(Parent1, Parent2):
def child_method(self):
print("자식 클래스 메서드")
# 객체 생성
child = Child()
child.method1() # Parent1에서 상속받은 메서드
child.method2() # Parent2에서 상속받은 메서드
child.child_method() # 자식 클래스 메서드
✅ Child 클래스는 Parent1, Parent2 두 개의 부모 클래스를 상속받았으므로 모두 사용 가능하다.
🔹 issubclass(자식클래스, 부모클래스)
print(issubclass(Student, Person)) # True
print(issubclass(Dog, Animal)) # True
print(issubclass(Dog, Person)) # False
🔹 isinstance(객체, 클래스)
s = Student("이영희", 21, "2024002")
print(isinstance(s, Student)) # True
print(isinstance(s, Person)) # True (Student가 Person을 상속했으므로)
print(isinstance(s, Dog)) # False
✅ 상속(Inheritance): 기존 클래스(부모 클래스)의 속성과 메서드를 새로운 클래스(자식 클래스)가 물려받음
✅ super(): 부모 클래스의 생성자(init)나 메서드를 호출할 때 사용
✅ 메서드 오버라이딩(Method Overriding): 부모 클래스의 메서드를 자식 클래스에서 재정의
✅ 다중 상속(Multiple Inheritance): 하나의 클래스가 여러 부모 클래스를 상속받을 수 있음
✅ issubclass(), isinstance(): 클래스 상속 관계 확인
📌 “객체가 생성될 때 자동으로 실행되는 특수한 메서드!”
✅ 예제: __ init __( ) 사용하기
class Person:
def __init__(self, name, age): # 생성자
self.name = name # 속성 설정
self.age = age
# 객체 생성 (생성자 실행됨!)
p1 = Person("홍길동", 25)
p2 = Person("김철수", 30)
print(p1.name, p1.age) # 출력: 홍길동 25
print(p2.name, p2.age) # 출력: 김철수 30
✅ p1과 p2를 만들 때 __ init __( )이 실행되어 자동으로 name과 age 속성이 설정됨!
📌 “객체마다 고유한 정보를 저장하는 변수!”
✅ 예제: 객체 속성
class Car:
def __init__(self, brand, color):
self.brand = brand # 자동차 브랜드
self.color = color # 자동차 색상
# 객체 생성
car1 = Car("현대", "빨강")
car2 = Car("기아", "파랑")
print(car1.brand, car1.color) # 출력: 현대 빨강
print(car2.brand, car2.color) # 출력: 기아 파랑
✅ car1과 car2는 각각 다른 속성 값(브랜드, 색상)을 가진 독립적인 객체야.
📌 “부모 클래스의 기능을 자식 클래스에서 가져올 때 사용!”
✅ 예제: super()를 사용한 상속
class Animal:
def __init__(self, species):
self.species = species # 동물 종류 설정
class Dog(Animal): # Animal을 상속받음
def __init__(self, species, name):
super().__init__(species) # 부모 클래스의 생성자 호출
self.name = name # 추가 속성
dog = Dog("포유류", "바둑이")
print(dog.species) # 출력: 포유류 (부모 클래스에서 상속)
print(dog.name) # 출력: 바둑이 (자식 클래스에서 추가)
✅ super()._ init __ (species)를 사용해서 부모 클래스의 __init __()을 실행!
✅ Dog 클래스는 부모 속성 + 추가 속성을 가짐.
🔹 “하나의 클래스가 두 개 이상의 부모 클래스를 상속받는 것”
✔ 일반적인 상속은 부모 클래스 하나만 상속받지만, 다중 상속은 여러 부모 클래스로부터 기능을 물려받을 수 있음.
✔ 여러 클래스를 조합해서 더 강력한 클래스를 만들 때 유용함.
✔ 다중 상속을 사용하면 여러 부모 클래스의 속성과 메서드를 모두 활용할 수 있음.
1️⃣ 다중 상속 기본 구조
class Parent1:
def feature1(self):
return "👨👩👧 부모1의 기능"
class Parent2:
def feature2(self):
return "🧑🎨 부모2의 기능"
# 다중 상속
class Child(Parent1, Parent2):
def feature3(self):
return "👶 자식 클래스의 기능"
# 객체 생성
child = Child()
print(child.feature1()) # 👨👩👧 부모1의 기능
print(child.feature2()) # 🧑🎨 부모2의 기능
print(child.feature3()) # 👶 자식 클래스의 기능
✅ Child 클래스는 Parent1과 Parent2의 기능을 모두 상속받아 사용할 수 있음.
super()는 부모 클래스의 메서드를 호출할 때 사용되는데, 다중 상속에서는 어느 부모 클래스의 메서드가 먼저 실행될지 중요함.
class Parent1:
def __init__(self):
print("👨👩👧 Parent1 생성자 호출")
class Parent2:
def __init__(self):
print("🧑🎨 Parent2 생성자 호출")
class Child(Parent1, Parent2):
def __init__(self):
super().__init__() # 부모 생성자 호출
print("👶 Child 생성자 호출")
child = Child()
👨👩👧 Parent1 생성자 호출
👶 Child 생성자 호출
✅ super()는 첫 번째 부모(Parent1)의 init()만 호출되었음!
✅ 다중 상속에서는 MRO(Method Resolution Order)라는 규칙을 따름.
4️⃣ super()를 다중 호출하려면?
위 예제에서는 super()를 사용했을 때 Parent1만 호출되고 Parent2는 호출되지 않았음.
➡ 이를 해결하려면 모든 부모 클래스에서 super()를 호출해야 함!
class Parent1:
def __init__(self):
super().__init__()
print("👨👩👧 Parent1 생성자 호출")
class Parent2:
def __init__(self):
super().__init__()
print("🧑🎨 Parent2 생성자 호출")
class Child(Parent1, Parent2):
def __init__(self):
super().__init__()
print("👶 Child 생성자 호출")
child = Child()
🧑🎨 Parent2 생성자 호출
👨👩👧 Parent1 생성자 호출
👶 Child 생성자 호출
✅ 모든 부모 클래스에서 super().init()을 호출하면 MRO 순서에 따라 모든 부모 클래스의 생성자가 실행됨!
5️⃣ 다이아몬드 상속 문제 (Diamond Problem)
다중 상속을 사용할 때, 부모 클래스가 같은 조상을 공유하면 메서드가 중복 호출될 수 있음.
💡 다이아몬드 상속 예제
class A:
def show(self):
print("A 클래스")
class B(A):
def show(self):
print("B 클래스")
class C(A):
def show(self):
print("C 클래스")
class D(B, C): # B와 C는 둘 다 A를 상속
pass
d = D()
d.show()
B 클래스
✅ D는 B와 C를 상속받았지만, MRO에 따라 왼쪽의 B가 먼저 실행됨!
✅ 다이아몬드 문제 해결 방법: super()를 올바르게 사용하면 해결됨.
📌 다중 상속 정리
✔다중 상속 :하나의 클래스가 여러 부모 클래스를 상속
class C(A, B):
✔super() :부모 클래스의 메서드를 호출할 때 사용
super().__init__()
✔MRO :메서드 탐색 순서
Class.mro()
✔다이아몬드 문제 :같은 조상을 공유하는 다중 상속 문제
class D(B, C):
🚀 한 줄 요약!
✔ 다중 상속은 여러 부모 클래스의 기능을 자식 클래스에서 동시에 사용할 수 있도록 해줌!
✔ super()를 사용하면 MRO 순서에 따라 부모 클래스가 호출됨.
✔ 다이아몬드 문제를 해결하려면 super()를 모든 부모 클래스에서 올바르게 호출해야 함.
✔ “부모 클래스로부터 물려받은 메서드를 자식 클래스에서 재정의(덮어쓰기)하는 것”
✔ 부모 클래스와 같은 이름, 같은 매개변수를 가지지만 동작을 다르게 변경
✔ 상속 관계에서만 발생
class Parent:
def say_hello(self):
return "👴 부모: 안녕하세요!"
class Child(Parent):
def say_hello(self): # 부모의 메서드를 재정의 (오버라이딩)
return "👦 자식: 안녕!"
child = Child()
print(child.say_hello()) # 출력: 👦 자식: 안녕!
✔ “같은 이름의 메서드를 여러 개 정의하는 것”
✔ 매개변수 개수나 타입이 다를 때, 다른 방식으로 동작
✔ 파이썬에서는 직접 지원되지 않음!
🚨 파이썬에서는 오버로딩을 직접 지원하지 않음!
C++, Java 같은 언어에서는 가능하지만, 파이썬에서는 같은 이름의 메서드를 여러 개 만들면 덮어씌워진다.
class Example:
def add(self, a, b):
return a + b
def add(self, a, b, c): # 이전 `add`를 덮어씌움 (오버로딩 불가)
return a + b + c
ex = Example()
print(ex.add(1, 2)) # 오류 발생! (오버로딩이 안됨)
✅ 해결 방법: 기본값을 설정하거나, *args 사용
class Example:
def add(self, a, b, c=0): # 기본값 설정으로 오버로딩처럼 구현
return a + b + c
ex = Example()
print(ex.add(1, 2)) # 출력: 3
print(ex.add(1, 2, 3)) # 출력: 6
class Example:
def add(self, *args): # 여러 개의 인자 처리 가능
return sum(args)
ex = Example()
print(ex.add(1, 2)) # 출력: 3
print(ex.add(1, 2, 3)) # 출력: 6
print(ex.add(1, 2, 3, 4)) # 출력: 10
🚀 오버라이딩 vs 오버로딩 차이 정리
오버라이딩 (Overriding)
✔ 개념: 부모 클래스의 메서드를 자식 클래스에서 덮어쓰기
✔ 어디서 사용?: 상속에서만 사용 가능
✔ 메서드 이름: 같아야 함
✔ 매개변수: 같음 (변경 불가)
✔ 파이썬 지원 여부: ✅ 지원
오버로딩 (Overloading)
✔ 개념: 같은 이름의 메서드를 여러 개 정의
✔ 어디서 사용?: 일반적으로 같은 클래스 내에서 사용
✔ 메서드 이름: 같아야 함
✔ 매개변수: 개수, 타입이 다를 수 있음
✔ 파이썬 지원 여부: ❌ 직접 지원 안됨 (대체 방법 필요)
📌 한 줄 요약
🔹 “객체를 직접 생성할 수 없고, 반드시 상속해서 사용해야 하는 클래스”
✔ 공통적인 기능을 정의하지만, 구체적인 구현은 자식 클래스에서 강제하도록 하는 클래스
✔ “틀(템플릿)” 역할을 하며, 상속받은 클래스는 반드시 특정 메서드를 구현해야 함
✔ abc (Abstract Base Class) 모듈을 사용하여 구현
from abc import ABC, abstractmethod # 추상 클래스 관련 모듈
# 추상 클래스 정의
class Animal(ABC):
@abstractmethod # 추상 메서드 (반드시 구현해야 함)
def make_sound(self):
pass # 구현 X → 자식 클래스에서 구현해야 함
# 자식 클래스에서 반드시 추상 메서드를 구현해야 함!
class Dog(Animal):
def make_sound(self):
return "🐶 멍멍!"
class Cat(Animal):
def make_sound(self):
return "🐱 야옹!"
# 객체 생성
dog = Dog()
cat = Cat()
print(dog.make_sound()) # 출력: 🐶 멍멍!
print(cat.make_sound()) # 출력: 🐱 야옹!
✅ Animal 클래스는 추상 클래스이므로 직접 객체를 생성할 수 없음!
✅ Dog와 Cat 클래스는 반드시 make_sound() 메서드를 구현해야 함.
2️⃣ 추상 클래스를 상속받아 구현하기
자식 클래스에서 추상 메서드를 반드시 구현해야 함!
class Dog(Animal): # Animal을 상속받음
def make_sound(self): # 반드시 구현해야 함!
return "🐶 멍멍!"
class Cat(Animal):
def make_sound(self): # 반드시 구현해야 함!
return "🐱 야옹!"
# 객체 생성
dog = Dog()
cat = Cat()
print(dog.make_sound()) # 출력: 🐶 멍멍!
print(cat.make_sound()) # 출력: 🐱 야옹!
✅ Dog, Cat 클래스는 Animal을 상속받아 반드시 make_sound()를 구현해야 함.
✅ 만약 구현하지 않으면 에러 발생! (TypeError: Can't instantiate abstract class)
3️⃣ 추상 클래스의 특징
🚨 (1) 추상 클래스는 직접 객체를 생성할 수 없음
a = Animal() # 오류 발생! (TypeError: Can't instantiate abstract class Animal)
✅ 추상 클래스는 “설계도” 역할을 하기 때문에 직접 객체 생성이 불가능!
✅ 반드시 자식 클래스에서 상속 후, 추상 메서드를 구현해야 사용 가능!
4️⃣ 추상 클래스에서 일반 메서드도 포함 가능
추상 클래스는 추상 메서드뿐만 아니라 일반 메서드도 가질 수 있음.
class Animal(ABC):
@abstractmethod
def make_sound(self):
pass
def sleep(self): # 일반 메서드 (자식 클래스에서 그대로 사용 가능)
return "😴 동물이 잠을 잔다."
class Dog(Animal):
def make_sound(self):
return "🐶 멍멍!"
dog = Dog()
print(dog.make_sound()) # 출력: 🐶 멍멍!
print(dog.sleep()) # 출력: 😴 동물이 잠을 잔다.
✅ sleep()은 일반 메서드이므로 자식 클래스에서 그대로 사용할 수 있음.
5️⃣ 추상 클래스를 활용하는 이유
💡 “강제성 부여 & 코드 일관성 유지”
✔부모 클래스를 상속받은 모든 자식 클래스가 반드시 특정 메서드를 구현하도록 강제함.
✔공통적인 기능을 추상 클래스로 정의하면, 유지보수가 쉬워짐.
✔다형성(Polymorphism)을 활용할 수 있음.
animals = [Dog(), Cat()] # 리스트에 여러 객체 저장 가능 (다형성)
for animal in animals:
print(animal.make_sound()) # 서로 다른 객체가 같은 메서드를 호출
✅ make_sound()를 모든 자식 클래스가 구현해야 하므로, animals 리스트에서 일관된 방식으로 호출 가능.
📌 정리
✔ 추상 클래스 : 직접 객체를 만들 수 없는 클래스 (ABC 사용)
✔ 추상 메서드 : 자식 클래스에서 반드시 구현해야 하는 메서드 (@abstractmethod)
✔ 일반 메서드 포함 가능 : 추상 클래스에서도 일반 메서드를 정의할 수 있음
✔ 객체 생성 불가 : TypeError 발생 (Animal()처럼 직접 인스턴스화 불가)
✔ 사용 이유 : 코드 강제성 부여, 유지보수 편리, 다형성 활용
🚀 한 줄 요약!
✅ 추상 클래스는 “설계도” 같은 역할을 하며,
자식 클래스에서 반드시 구현해야 하는 메서드를 정해줌!
✅ 객체를 직접 만들 수 없고, 상속받은 자식 클래스에서만 사용 가능!
✅ 추상 클래스를 사용하면 코드의 일관성이 유지되고, 유지보수가 편리해짐!