[파이썬] 08. 클래스, 객체지향 프로그래밍
객체
상태 : 변수, 객체의 속성 == 필드
동작 : 함수, 객체가 취할 수 있는 동작 == 메소드
객체 : 필드 + 메소드
클래스란?
- 객체에 대한 설계도를 의미한다
- 인스턴스 : 클래스로부터 만들어지는 각 객체를 그 클래스의 인스턴스(instance)라 한다
- 메소드 : 클래스 내의 함수를 의미한다
클래스 구조 이해하기
우선.. 클래스의 기본 구조는 이렇다
# 1. 클래스 호출
class Television:
# 2. __init__ : constructor(생성자) 설정
def __init__(self, channel, volume, on):
self.channel = channel
self.volume = volume
self.on = on
# 메소드1 : 입력값을 그대로 보여주는 기능
def show(self):
print(self.channel, self.volume, self.on)
# 메소드2 : 값을 변경하는 기능
def setChannel(self, channel):
self.channel = channel
# 메소드3 : 값을 get하는 기능
def getChannel(self):
return self.channel
1. 클래스 호출하기
class 클래스이름 :
2. 생성자 설정하기
def __init__(self, 매개변수1, 매개변수2, ...):
self.매개변수1 = 매개변수1
self.매개변수2 = 매개변수2
3. 메소드 만들기
인스턴스 메소드 중에서는
- 값을 그대로 보여주기
- 값을 변경하기
- 값을 얻어오기
세 가지가 기본적이고, 가장 대표적이다
하나씩 살펴보자
메소드 (Method)
클래스 안에서 어떤 기능을 수행하는 함수, 객체 정보를 지정할 수 있다
class Triangle :
def setData(self, b, h) : #메소드
self.b = b
self.h = h
tri1 = Triangle() #객체 생성
tri1.setData(4, 5) #객체 메소드 실행
print(tri1.b, tri1.h)
- 메소드 선언하는 방법
def 메소드이름(self, 매개변수1, 매개변수2, ...):
- 메소드 실행하는 방법
객체이름 = 클래스이름() # 객체 생성
객체이름.메소드이름(매개변수1, 매개변수2, ...) # 객체 메소드 실행
- self란 무엇인가?
setData 메소드의 매개변수 self는 전달인자를 입력받지 않는다. 대신 객체 tri1을 전달받는다.
-> 때문에 self.b == tri1.b
-> self.h == tri1.h라고 볼 수 있다
- 해석하면 self에 객체 tri1이 걸쳐서 tri1.b = 4가 할당된다
- 'self.변수이름' 형태로 사용하는 변수를 인스턴스 변수라고 한다
- 인스턴스 변수는 각 객체의 값으로 볼 수 있다.
- 때문에 tri2가 만들어진다해도 tri1의 b,h 값은 공유되지 않는다
__init__, 생성자 (Constructor)
constructor. 객체가 생성될 때 객체를 기본값으로 초기화하는 특수 메소드이다
-> initialization의 역할을 한다
->
def __init__(self, 매개변수1, 매개변수2, ...):
클래스 변수
클래스 안에서 선언된 변수로, 클래스에 종속된 변수이다
여러 객체를 생성했을 때, 어떤 객체여도 동일한 값이 담긴다
class Triangle :
height = 10 # 클래스 변수
bottom = 4
tri1 = Triangle()
print(tri1.height)
print(tri1.bottom)
tri2 = Triangle()
print(tri2.height)
print(tri2.bottom)
tri1, tri2는 각각 다른 객체이지만, 같은 값이 프린트된다
height와 bottom이 클래스 변수이기 때문
메소드의 종류
1) 인스턴스 메소드
- self가 들어간 경우
- 위의 예제코드에서 쓰인 세 가지 메소드가 인스턴스 메소드에 해당한다
- 객체가 갖는 저마다의 고유한 속성 값을 사용한다
- 가장 흔하게 쓰이는 메소드 형태
class Triangle :
cal_count = 0
def __init__(self, b, h = 5) : # 생성자
self.b = b
self.h = h
def area(self) : # 인스턴스 메소드
# 클래스 변수를 수정한다
Triangle.cal_count += 1
# self.cal_count += 1 <- 객체 내에서만 cal_count를 수정한다
# 만약 이렇게 작성하면 print(Triangle.cal_count) = 0이 출력됨
return self.b * self.h / 2
tri1 = Triangle(4) #밑변이 4인 삼각형 객체 생성
print(tri1.b, tri.h, tri1.area())
print(tri1.cal_count)
print(Triangle.cal_count) # Triangle.cal_count +=1를 해줬으므로 1이 출력됨
2) 정적 메소드
- @staticmethod 데코레이터 사용
- 아무것도 인자로 받지 않는 메소드이다
- 인스턴스 필드에 접근하지 않는다 => 즉, self 매개변수를 갖지 않음
# 클래스 선언
class Triangle :
cal_count = 0 # 클래스 변수에 해당함
def __init__(self, b, h = 5) : # 생성자
self.b = b
self.h = h
def area(self) :
Triangle.cal_count += 1
return self.b * self.h / 2
# 정적 메소드 선언
@staticmethod
def isIsosceles(a, b) :
Triangle.cal_count += 1
return a == b
tri1 = Triangle(4) #밑변 4 삼각형 객체 생성
# 정적 메소드는 어떻게 사용하느냐?
print(tri1.isIsosceles(5,5)) # 객체.정적메소드(전달인자1, 전달인자2) <- posiible
print(Triangle.isIsosceles(5,5)) # 클래스.정적메소드(전달인자1, 전달인자2) <- possible
-> self.~ 형태로 쓰이지 않는다는 점이 정적 메소드의 가장 큰 구분점이다
-> 그렇다고 '객체.정적메소드'가 불가하다는 뜻은 아니다
-> 맨 아래처럼 정적메소드는 객체.로 붙이든 클래스.로 붙이든 수행된다
=> 중요한 점 !!!!!
tri1.isIsosceles <- cal_count 클래스 변수 값 자체를 바꾸지 않음. 객체 내부에서만 +1이 된다
Triangle.isIsosceles <- cal_count 클래스 변수 값 자체를 바꿈. 다른 객체를 선언하고 cal_count를 불러보면 +1이 되어있다
3) 클래스 메소드
- @classmethod 데코레이터 사용
- 클래스 변수를 control 할 때 사용한다
- cls로 클래스 변수를 전달 받는다
-> 클래스 변수에 접근하기 위한 메소드
class Triangle :
cal_count = 0
def __init__(self, b, h = 5) :
self.b = b
self.h = h
@classmethod #클래스 메소드 선언
def printCount(cls) : # cls를 통해 접근
cls.cal_count += 1
return cls.cal_count
tri1 = Triangle(4)
print(tri1.printCount()) #인스턴스로 접근
print(Triangle.printCount()) #클래스로 직접 접근
-> 방금 전 정적 메소드와 비교해보자
[ 정적 메소드 ]
tri1.정적메소드() <- 클래스 변수인 cal_count의 실제값을 바꾸지 못했다
Triangle.정적메소드() <- 이렇게 클래스 단위로 접근해야 cal_count의 실제값을 바꿀 수 있었다
[ 클래스 메소드 ]
tri1.클래스메소드() <- 객체. 으로 접근해도 클래스 변수 cal_count의 실제값을 바꾼다
Triangle.클래스메소드() <- 당연히 cal_count의 값을 바꾼다
-> 솔직히 왜 이렇게까지 하는가? 아직 이해가 잘 안된다
-> 하지만 코딩이라는게.. 구조를 짜다보면 생각외로 아주 복잡해지니까.. 쓸 일이 있어서 메소드를 구분해놨겠지 싶다
+) 매직 메소드
__init__ 처럼, 종종 파이썬에서는 언더바 (__)가 사용된다
언더바의 자세한 사용은 https://gomguard.tistory.com/125
[Python] 언더바 (_) 사용하기
파이썬의 언더바 파이썬에선 (_) 를 사용하는 경우들이 있습니다. 1. 인터프리터에서 마지막 값을 저장하고 싶을 때 2. 값을 무시하고 싶을 때 3. 변수나 함수명에 특별한 의미를 부여하고 싶을 때
gomguard.tistory.com
아무튼 __를 활용한 메소드를 '매직 메소드'라 부른다
__str__
__repr__
__dir__
__dict__
__doc__
클래스 상속
코딩을 하다보면 같은 속성을 나타내기 위해 불필요한 반복이 나타나는 경우가 많다
이럴 때 '상속'을 통해 두 번 코드를 짤 필요없이 원하는 속성을 물려받을 수 있다
class 부모클래스 :
...
class 자식클래스(부모클래스):
...
위와 같은 형식으로 원하는 클래스의 속성을 복사(상속)할 수 있으며, 상속받는 클래스가 자식클래스이다
자식 클래스는 부모 클래스의 메소드, 변수를 가져와 사용할 수 있고, 메소드를 overriding(재정의) 할 수도 있다
예를 들어, 이런 구조로 클래스를 상속하고 싶다
import math
#도형
class Shape :
cal_count = 0
figure = "Shape"
@classmethod
def class_printFigure(cls) :
return cls.figure
@staticmethod
def static_printFigure() :
return Shape.figure
#도형 상속 삼각형
class Triangle(Shape) :
figure = "triangle"
def __init__(self, b, h=5) :
self.b = b
self.h = h
def area(self) :
Shape.cal_count += 1
return self.b * self.h / 2
#도형 상속 정삼각형
class EquTriangle(Triangle) :
figure = "equilateral triangle"
def __init__(self, b) :
self.b = b
def area(self) :
Shape.cal_count += 1
return 0.25 * math.sqrt(3) * self.b ** 2
def circumference(self) :
return self.b * 3
tri = Triangle(10, 4)
eqtri = EquTriangle(3)
print(tri.class_printFigure(), tri.area(), cir.circumference())
print(eqtri.class_printFigure(), eqtri.area(), cir.circumference())
print(Shape.cal_count, eqtri.cal_count, tri.cal_count)