Goorm 자연어처리 전문가 양성 과정 2기/Python

[파이썬] 08. 클래스, 객체지향 프로그래밍

슈빔멘 2022. 1. 21. 15:09

객체

상태 : 변수, 객체의 속성 == 필드

동작 : 함수, 객체가 취할 수 있는 동작 == 메소드

객체 : 필드 + 메소드

 

클래스란?

- 객체에 대한 설계도를 의미한다

- 인스턴스 : 클래스로부터 만들어지는 각 객체를 그 클래스의 인스턴스(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)