본문 바로가기
Programming/python

클래스(class)와 객체 지향 프로그래밍(object oriented programming) 개념 정리

by 조창대 2022. 6. 13.
반응형

* 객체 지향 프로그래밍 ( Object Oriented Programming)

프로그래밍을 공부하는 사람이라면, ' 객체 지향'이라는 단어는 질리도록 들어봤을 것이다.

요즘 많이 쓰이는 C++, 자바, 파이썬의 기본 로직이기 때문이다. 면접에서도 "객체 지향 프로그래밍을 설명해주세요."라는 단골 질문이 많이 나오지만 정립하기 쉬운 개념이 아니라 꼭! 개별적인 정리가 필요하다.

 

"객체 지향 프로그래밍은 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러 개의 독립된 단위, 즉 "객체"들의 모임으로 파악하고자 하는 것이다. 각각의 객체는 메시지를 주고받고, 데이터를 처리할 수 있다." 라고 위키백과에 명시되어 있다.

즉, 기존의 절차 지향 프로그래밍은 데이터를 순차적으로 처리하고, 데이터들이 모두 유기적으로 연결되어 있다. 하지만 점차 소프트웨어가 발전하면서 데이터의 쓰임이 증가하고, 데이터를 다양하게 변형하여 사용해야 하는데 모두 유기적으로 연결되어 있어 생산 효율이 매우 떨어지게 된다.

 

이러한 단점을 보완하고자 나온 것이 '객체 지향'이라는 개념이다. 프로그래밍에 필요한 추상적인 개념(클래스)에 의해서 자료(data)와 메서드(method)를 가진 객체를 생성하고, 독립적인 메모리 주소를 가진 객체들이 상호작용을 통해 로직을 구현하는 기법이라고 정리할 수 있겠다. 

 

그렇다면 클래스는 뭐고 객체는 무엇일까라는 의문이 끝없이 생긴다. 니가 뭔데 날 힘들게 해 !!

 

 

 

* 클래스와 객체의 관계

클래스는 프로그램을 이용하여 객체를 만들어주는 역할을 하는 물리적이지 않은, 추상적인 개념이다. 

클래스가 레시피라고 해보자. 우리는 요리를 하기 전에 레시피를 본다. 레시피에는 요리할 때 필요한 식자재, 요리방법 등이 담겨있지만 요리를 하기 전에는 음식이 만들어지지 않는다. 음식을 만들기 전에는 레시피는 추상적인 설계도에 불과하다. 어찌보면 당연한 논리가 클래스와 객체에도 적용된다.

 

객체는 클레스에 의해서 만들어지는 결과물이다. 위에 레시피 예로 설명하자면 객체는 음식이다.

썰기, 자르기, 섞기 등 레시피에 명시된 요리방법을 이용하여 식자재를 이리저리 조작하며 음식을 완성한다.

클래스와 객체의 관계도 이와 같다. 클래스에는 객체를 만들기 위한 속성(자료, 데이터, 변수)과 행위(기능, 메소드)가 집합해있다. 속성은 식자재, 행위는 요리방법과 일치한다. 

그래서 하나의 레시피를 보고도 여러 개의 음식을 만들 수 있듯이, 클래스와 객체도 1 : N의 관계를 갖는다. 하나의 클래스로 데이터와 메소드를 다양하게 혼합하여 여러 가지의 객체를 생성할 수 있다.

 

참고로, 인스턴스와 객체를 혼용해서 표기하는 경우가 많은데
인스턴스는 갓 만들어진 객체를 뜻한다는 점에서 객체와의 차이가 있다. 예를 들어, Calc 클래스에 의해서 a 객체가 만들어졌을 때, a는 객체 or 인스턴스라고 할 수 있다. 그 다음으로 b 객체가 만들어졌을 때, b 객체가 인스턴스가 되고 a는 인스턴스라고 할 수 없어진다.
즉, 인스턴스는 객체에 현재성을 부여한 개념이라고 할 수 있다. 

 

 

 

* 클래스 구성 요소

클래스는 객체를 만들 설계도면이다. 클래스는 객체를 만들 때 필요한 속성과 행위의 집합이라고 앞에서 설명했는데,

클래스의 구성요소로 멤버변수, 생성자, 메서드가 있다. 

이를 코드로 구현하고 구성 요소를 하나씩 살펴보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Calc:
    num1 = num2 = 0
    # 생성자
    def __init__(self, x, y):
        self.num1 = x
        self.num2 = y
 
    # 곱셈 메소드
    def mul(self):
        return self.num1 * self.num2
    
    # 나눗셈 메소드
    def div(self):
        return self.num1 / self.num2
 
# 객체 생성하고 참조변수에 할당
obj = Calc(10,5)
print('곱셈 :', obj.mul())
print('나눗셈 :', obj.div())
cs

 

1라인 : 함수를 정의할 땐 'def 함수명(파라미터1, 파라미터2)' 의 형태지만, 클래스를 정의할 땐 'class 클래스명' 으로 시작한다.

2라인 : Calc 클래스 선언부로, 클래스의 멤버변수( num1, num2 ) 2개를 선언한다. 이 때 멤버변수의 초기값은 0 이거나 null로 해야한다. 클래스 멤버변수는 일반 변수나 함수와 구분하기 위한 클래스만의 변수라고 할 수 있다. 쉽게 말해서 클래스 안에서만 연산, 활용할 수 있는 변수이다. 

클래스 안에서 멤버변수를 사용할 땐 'self.num1' 과 같이 변수명 앞에 'self'를 붙여야 한다. 클래스에 속한 멤버라는 걸 명시하기 위함이다.

4라인 ~ 6라인 : 클래스 생성자 부분이다. 자바는 생성자를 클래스명으로 쓰지만, 파이썬의 생성자는 함수의 형태로 '__init__'이라는 별도의 생성자로 선언한다. 생성자는 객체를 생성하고 멤버변수를 초기화하는 역할을 맡는다. 코드 16라인이 실행되면 바로 Calc(10,5)에서 인수가 Calc 클래스의 생성자 __init__의 매개변수에 할당되면서 생성자로 인해 객체가 생성된다. 이어서 5번, 6번 라인이 실행되며  self.num1, self.num2라는 멤버변수에 각각 x-->(10), y-->(5)가 할당되며 값이 초기화된다. 매번 객체가 생성될 때마다  __init__의 매개변수인 x,y에 인수가 할당되고 x,y가 멤버변수인 num1, num2에 할당되면서 또 다른 객체가 만들어지는 논리이다. 

8라인 ~ 14라인 :  클래스 안에서 메소드를 정의할 땐 꼭! 첫번째 매개변수로 'self'를 넣어줘야 한다. 클래스 안에서 정의하는 것이라는 일종의 표식이라고 생각하면 된다. 설령 매개변수를 갖지 않는 메소드라도 self는 꼭 넣어주자.

곱셈과 나눗셈 메소드는 멤버변수 num1과 num2를 각각 연산하고 연산결과를 return으로 반환한다.

17라인 : 매우매우매우매우 중요한 대목이다. 앞에서 클래스는 물리적으로 존재하지 않는, 추상적인 개념이라고 했다. 이 코드가 없으면 클래스는 계속 추상적 개념으로만 존재한다. 17라인을 실행함으로써 객체가 생성[ Calc(10,5) ] 되고, 생성된 객체의 메인 메모리 주소가 obj라는 참조변수에 할당된다. 

         obj에 객체가 아닌 '객체의 메모리 주소' 가 할당된다는 개념이 매우 중요하다. 

Calc(10,5) 코드로 인해 클래스가 호출되고, 생성자 매개변수 x, y에 10 ,5가 각각 할당된다. 값이 할당된 x, y는 클래스 멤버변수인 num1, num2에 할당되어 새로운 객체가 생성된다. 생성된 객체 안에는 클래스의 생성자, 멤버변수, 메소드가 모두 존재한다!

이렇게 생성된 객체는 메인 메모리의 한 켠의 주소를 가지고 있는데 이를 obj에 할당함으로써 obj는 객체의 주소를 가르키고 있는 셈이다. 

18라인 ~ 19라인 : 17라인 설명에서 객체는 생성자, 멤버변수, 메소드를 가진다고 했다. 그래서 obj에 객체 주소를 할당하면 obj로 메소드에 접근할 수 있다. '참조변수.클래스 메소드' 의 형태로 연산이 가능해진다는 소리다. 그래서 코드에서도 obj.mul() , obj.div() 로 연산결과를 반환할 수 있었다. 

 

 

클래스와 객체, 참조변수 설명 그림

 

 

위 그림은 앞선 코드에서 설명한 참조변수와 객체의 관계를 나타낸다. 

참조변수인 클래스로부터 객체가 만들어지면 이 객체는 #100(예시) 라는 메모리 주소를 갖고, obj 참조변수는 #100을 가르킨다.

 

 

* 생성자 생략 가능

'__init__' 생성자를 생략할 수도 있다.하지만 생성자가 객체 생성과 멤버 변수 초기화를 담당하는 중요한 역할이기 때문에 생성자 역할을 대행하는 메소드가 필요하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Calc:
    # 멤버변수 없음
    # 생성자 없음
    # def __init__(self):
        # pass
    # 객체 생성, 동적 멤버변수 생성, 초기화
    def data(self, x, y):
        self.num1 = x
        self.num2 = y
 
    # 곱셈 메소드
    def mul(self):
        return self.num1 * self.num2
 
obj = Calc() # 객체 생성
obj.data(10,5# 멤버변수 할당
obj.mul()
cs

data 메소드가 생성자 역할을 대행하는 코드이다. 생성자에 있던게 그대로 메소드로 가서 멤버변수가 초기화된다.

이를 동적 멤버변수 생성/초기화 한다고 한다. 

생성자를 생략하면 프로그램에서 자동으로 __init__(self): pass 라는 생성자를 생략한다는 뜻의 코드를 실행한다.

 

 

클래스와 객체는 객체지향프로그래밍에서 나오는 용어들이니, 꼭 연관지어서 개념을 정립할 필요가 있다.

다음 포스팅에선 객체지향프로그래밍의 특징인 캡슐화, 상속, 재정의, 다형성에 대해 알아보겠다.

반응형