1. 클래스
파이썬은 다른 객체 지향 언어보다 더 유연하고 동적인 클래스 시스템을 가지고 있다.
1.1. 기본 클래스 구조
파이썬 클래스는 class 키워드로 정의하며, 콜론과 들여쓰기로 클래스 내용을 구분한다.
class 클래스명:
def 메서드명1(self, 매개변수1, 매개변수2, ...):
명령1
명령2
...
클래스명은 일반적으로 PascalCase 표기법(첫 글자와 각 단어의 첫 글자를 대문자로)을 사용한다.
1.2. 인스턴스 생성과 초기화
클래스의 인스턴스는 클래스명을 함수처럼 호출하여 생성한다.
__init__ 메서드는 생성자로, 인스턴스가 생성될 때 자동으로 호출된다.
ex)
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
# 인스턴스 생성 및 초기화
rect = Rectangle(10, 5)
print(rect.area()) # 출력: 50
2. 인스턴스 변수와 클래스 변수
파이썬 클래스에는 인스턴스 변수와 클래스 변수 두 가지 유형의 변수가 있다.
2.1. 인스턴스 변수
인스턴스 변수는 각 인스턴스마다 별도로 존재하는 변수이다.
- 인스턴스 변수는 self.변수명 = 값 형태로 정의한다.
- self는 자기 자신(인스턴스)을 참조하는 변수이다.
- 메서드 안에서 self 없이 정의된 변수는 인스턴스 변수가 아닌 지역 변수이다.
ex)
class Dog:
def __init__(self, name, breed):
self.name = name # 인스턴스 변수
self.breed = breed # 인스턴스 변수
def bark(self):
sound = "멍멍!" # 지역 변수 (메서드 내에서만 사용)
return f"{self.name}: {sound}"
dog1 = Dog("바둑이", "진돗개")
dog2 = Dog("흰둥이", "말티즈")
print(dog1.name, dog1.breed) # 출력: 바둑이 진돗개
print(dog2.name, dog2.breed) # 출력: 흰둥이 말티즈
각 인스턴스는 고유한 인스턴스 변수를 가지므로, dog1.name과 dog2.name은 서로 다른 값을 가질 수 있다.
2.2. 클래스 변수
클래스 변수는 클래스의 모든 인스턴스가 공유하는 변수이다.
- 클래스 변수는 메서드 외부, 클래스 내부에 정의된다.
- 클래스 변수는 클래스명.변수명 또는 self.__class__.변수명으로 접근할 수 있다.
ex)
class Dog:
# 클래스 변수
species = "개"
count = 0
def __init__(self, name):
self.name = name # 인스턴스 변수
Dog.count += 1 # 클래스 변수 수정
def get_info(self):
return f"{self.name}은(는) {self.__class__.species}입니다."
dog1 = Dog("바둑이")
dog2 = Dog("흰둥이")
print(Dog.species) # 출력: 개
print(Dog.count) # 출력: 2
print(dog1.species) # 출력: 개 (클래스 변수를 인스턴스를 통해 접근)
print(dog2.get_info()) # 출력: 흰둥이은(는) 개입니다.
클래스 변수는 모든 인스턴스에서 공유되므로, 한 인스턴스에서 변경하면 모든 인스턴스에 영향을 미친다.
2.3. 인스턴스 속성 동적 추가
파이썬의 유연한 특성 중 하나는 인스턴스가 생성된 후에도 외부에서 새로운 속성을 추가할 수 있다는 점이다.
- 인스턴스명.새로운_변수명 = 값 형태로 클래스에 정의되지 않은 변수도 추가할 수 있다.
ex)
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
person = Person("홍길동", 30)
print(f"{person.name}, {person.age}") # 출력: 홍길동, 30
# 인스턴스에 새로운 속성 추가
person.job = "개발자"
print(f"{person.name}, {person.age}, {person.job}") # 출력: 홍길동, 30, 개발자
이 특성은 파이썬의 동적 특성을 보여주지만, 코드의 예측 가능성과 유지보수성을 위해 가급적 클래스 정의 시 모든 속성을 명시하는 것이 좋다.
3. 상속과 다형성
상속은 기존 클래스의 기능을 재사용하고 확장하는 메커니즘이다.
파이썬은 단일 상속과 다중 상속을 모두 지원한다.
3.1. 기본 상속
상속은 클래스 정의 시 괄호 안에 부모 클래스를 명시한다.
class 자식클래스(부모클래스):
# 클래스 내용
자식 클래스에서 부모 클래스의 메서드와 인스턴스 변수에 접근할 수 있다.
ex)
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return "소리를 낸다"
class Dog(Animal):
def speak(self):
return "멍멍"
class Cat(Animal):
def speak(self):
return "야옹"
dog = Dog("강아지")
cat = Cat("고양이")
print(f"{dog.name}가 {dog.speak()}") # 출력: 강아지가 멍멍
print(f"{cat.name}가 {cat.speak()}") # 출력: 고양이가 야옹
Dog와 Cat 클래스는 Animal 클래스를 상속받아 name 속성을 그대로 사용하면서 speak 메서드를 오버라이딩한다.
3.2. 부모 클래스 생성자 호출
자식 클래스에서 부모 클래스의 생성자를 호출하는 방법은 두 가지가 있다.
1) 부모 클래스 이름을 직접 사용.
class 자식(부모):
def __init__(self, 변수1, 변수2, ...):
부모.__init__(self, 변수1)
self.변수2 = 변수2
2) super() 함수 사용
def __init__(self, 변수1, 변수2, ...):
super().__init__(변수1)
self.변수2 = 변수2
super()를 사용하는 방법이 더 유연하고, 특히 다중 상속에서 동작 방식이 명확하다.
3.3. 다중 상속
파이썬은 여러 부모 클래스로부터 상속받는 다중 상속을 지원한다.
class 자식클래스(부모클래스1, 부모클래스2, ...):
# 클래스 내용
다중 상속 시 메서드 호출 순서는 MRO(Method Resolution Order)에 따라 결정된다.
MRO는 C3 선형화 알고리즘을 사용하여 상속 관계를 분석하고, 각 클래스의 __mro__ 속성을 통해 확인할 수 있다.
3.4. 메서드 오버라이딩
오버라이딩은 부모 클래스의 메서드와 동일한 이름으로 자식 클래스에서 메서드를 재정의하는 것이다.
자식 클래스에서 메서드를 새로 정의하면 부모 클래스의 메서드를 대체한다.
ex)
class Animal:
def speak(self):
return "동물이 소리를 냅니다"
class Cat(Animal):
def speak(self):
return "야옹"
class Lion(Cat):
def speak(self):
return "어흥"
animal = Animal()
cat = Cat()
lion = Lion()
print(animal.speak()) # 출력: 동물이 소리를 냅니다
print(cat.speak()) # 출력: 야옹
print(lion.speak()) # 출력: 어흥
오버라이딩된 메서드에서 부모 클래스의 메서드를 호출할 수도 있다.