-
파이썬의 객체와 클래스Python/study 2013. 12. 18. 03:16
※ 주의사항
아래 공격 코드는 연구 목적으로 작성된 것이며, 허가 받지 않은 공간에서는 테스트를 절대 금지합니다.
악의 적인 목적으로 이용할 시 발생할 수 있는 법적 책임은 자신한테 있습니다. 이는 해당 글을 열람할 때 동의하였다는 것을 의미합니다.
해당 문서의 저작권은 해당 저자에게 모두 있습니다. 다른 용도로 사용할 시 법적 조치가 가해질 수 있습니다.
이번에는 객체와 클래스에 대하여 알아보도록 하겠습니다.
먼저 객체와 클래스의 관계를 알아보도록 하겠습니다. 간단하게 그림을 통해 설명해보도록 하겠습니다.
위의 관계는 클래스와 객체의 관계를 보여줍니다. 사실 용어가 생소해서 그렇지 전혀 어렵지 않습니다. 먼저 사람의 형태를 하고 있는 그림이 보입니다. 이것은 남자인지, 여자인지, 학생인지 직장인인지 그 어떤 정보도 가지고 있지 않습니다. 단지 사람이라는 틀을 가지는 각각의 요소가 가져야 할 정보를 정의하고 있습니다. 이것이 바로 클래스입니다. 실체가 없지만, 어떤 요소들에 대한 공통적이고 일반적인 정의를 할 수 있는 개념인 것입니다.
그럼 다음으로 넘어가서 아래에 있는 '김남자'와 '최여자'는 어떻습니까? 사람이라는 클래스에서 정의한 요소들에 대한 정보가 저장되어 있습니다. 각각의 사람에 대한 정보를 가지고 있음으로써, 서로를 식별, 구분할 수 있고 사람이라는 공통적인 틀에 있으면서 각각이 유니크한 요소가 되는 것입니다. 이것이 바로 객체입니다. 즉, 사람이라는 포괄적인 개념을 나타내는 클래스에 속하면서, 클래스에서 정의한 요소들에 대한 개별적인 고유의 정보를 가진 실체, 즉 사람이라는 클래스의 객체입니다.
사람이라는 클래스는 실체가 아닌 추상적인 정의인 반면, 김남자, 최여자와 같은 객체는 실체입니다.
이해를 위해 한 가지만 더 예를 들어보도록 하겠습니다.
붕어빵을 만드는 것입니다. 붕어빵 틀은 붕어빵의 클래스로써, 붕어빵의 모양을 정의합니다. 그리고 우리는 붕어빵이 되려면 밀가루와 팥이 필요하다는 것을 알고 있습니다. 바로 붕어빵이라는 클래스에서 정의하는 붕어빵의 요소가 되는 것입니다. 붕어빵이 어떻게 생겼고, 어떤 요소가 필요한지 알고 있다고 해서 붕어빵이 있는 것은 아닙니다. 실체가 없는 것이죠.
실제로 붕어빵의 틀에 밀가루를 붓고 팥을 넣고 구우면 붕어빵이 만들어집니다. 실체가 생긴것이죠. 이것이 바로 객체입니다. 클래스에서 정의한 대로 만들어 낸 실체가 객체인 것입니다. '붕어빵틀 = 클래스, 붕어빵 = 객체', 이해가 되시리라 믿습니다.
정리 59) 클래스와 객체 : 클래스는 객체의 틀이 되는 추상적인 개념이고, 객체는 클래스에 정의된 요소들의 실체입니다.
또 하나 알아두어야 할 것이 있습니다. 바로 C언어와 파이썬이 데이터를 저장하는 방법의 차이입니다. 그림을 통해 알아보도록 하겠습니다.
절차지향적 언어인 C언어는 변수를 지정하면 정해진 공간에서 값을 쓰고 지웁니다. 반면, 객체지향적인 언어인 파이썬은 명시된 공간에 값이 직접적으로 저장되는 것이 아니라, 임의의 공간에 값이 쓰여지면 이름이 정해진 명시된 공간이 해당 값이 있는 공간을 가리키는 형식으로 값을 저장합니다. 즉, x라는 공간에 접근을 하면 x라는 공간이 가리키고 있는 공간의 값을 읽어오는 것입니다. 이 두 언어의 데이터 저장방식의 차이점을 숙지하시길 바랍니다.
그럼 이제부터 파이썬에서 사용되는 객체에 대하여 알아보도록 하겠습니다. 객체는 어떤 것일까요? 뭔가 특별하게 정의되어 있을까요? 그렇지 않습니다. 파이썬에서는 사용되는 모든 값들이 객체입니다.
위의 예제를 보면 일반적으로 사용하는 리스트를 생성하였고, append()함수를 사용하여 아이템을 추가하였습니다. 이 부분이 객체의 가장 큰 특징입니다. append()함수를 사용한 것 말입니다. 이것이 바로 객체가 가지는 특징입니다. 객체는 내부에 데이터를 저장할 공간과 해당 데이터와 관련된 메소드(함수)들을 가지고 있습니다. 객체를 사용하지 않는 C언어와 다른 부분입니다.
정리 60) 객체의 구조 : 객체는 내부에 데이터를 저장할 공간과 해당 데이터에 관련된 메소드들을 가지고 있습니다.
이해를 돕기위해서 C언어와 비교를 해보도록 하겠습니다.
왼쪽이 C언어로 작성한 것이고, 오른쪽이 파이썬으로 작성한 것입니다. 둘 다 똑같이 1,2,3,4,5가 저장된 배열(or 리스트)을 만들고 6이라는 값을 추가합니다. C언어에서는 추가할 데이터가 배열 내의 어느 위치에 저장이 될 것인지를 프로그래머가 직접 지정해주어야 합니다. 만약 사이즈가 커지고 들어가는 데이터 값들이 복잡해지면 굉장히 애를 먹겠지요.
하지만, 파이썬에서는 숫자 배열이 저장되어 있는 객체에 내장되어 있는 메소드를 호출하여 데이터를 추가하였습니다. 데이터가 추가될 위치가 어디인지 프로그래머는 알 필요가 없습니다. 그리고 다른 외부 함수를 불러올 필요도 없습니다. 또한 데이터를 추가할 객체를 다른 함수에 인자로 줄 필요도 없습니다. 해당 객체 내에 내장된 메소드를 사용하면 되기 때문입니다. 이것이 바로 객체 입니다.
우리가 이제껏 예제들을 통해 사용해왔던 모든 값들이 위와 같이 내장함수를 가지고 있습니다. 정수형을 저장한 공간, 튜플을 저장한 공간, 문자열을 저장한 공간, 리스트를 저장한 공간 모두 다 객체로써 데이터를 저장하고, 그와 관련된 메소드들을 가지고 있습니다. 그렇다면 그 메소드들은 어떤 것이 있는지 알아보려면 어떻게 해야 할까요?
위의 그림에 보이는 것처럼 dir()함수를 사용하면 됩니다. 해당 함수의 파라미터로 확인해보고 싶은 객체의 이름을 써주거나, 데이터 값을 넣어주면 해당 객체가 가지고 있는 메소드들의 이름을 출력해줍니다. 위의 예제에서는 리스트형 객체인 items가 데이터 값과 함께 가지고 있는 메소드들의 이름을 보여줍니다.
위에 나타난 정보를 보면 우리가 자주 사용한 append()라는 메소드도 보이고, __add__와 같이 이중 언더바로 감싸진 이름을 가진 메소드들도 보입니다. 하나의 객체가 이렇게 많은 메소드들을 가지고 있습니다. 이것은 저장 공간의 타입에 따라 주어지는 메소드들입니다. 만약 정수 값을 저장하는 공간에 대해서 dir()함수를 사용했다면, 정수형 데이터에 관련된 함수들이 출력되겠지요.
정리 61) dir(items) : 파라미터로 주어진 객체가 가지고 있는 메소드들의 이름을 보여줍니다.
조금 더 자세한 정보를 얻기위해서는 help()함수를 사용하면 됩니다. 그러면 아래와 같이 해당 객체의 타입에 정의된 데이터 저장 형태와 메소드들에 대한 정보가 더욱 상세하게 나타납니다.
내용이 너무 길어 중간부분은 생략을 했습니다. 앞서 사용했던 dir()함수가 객체 내 메소드들의 이름 만을 알려주었다면, help()함수를 사용하면 해당 객체가 저장하는 데이터의 타입도 알 수가 있습니다. 또한 각각의 메소드들을 어떻게 사용하는지 까지 알려줍니다.
정리 62) help(items) : 객체가 저장하는 데이터의 타입과 메소드에 대한 자세한 설명을 보여줍니다.
이런 기능을 통해 다음과 같이 items 객체의 구조를 파악할 수도 있습니다.
다음은 help()함수의 결과로 나온 메소드들의 사용법 중에서 가장 처음으로 나온 __add__ 메소드를 사용해본 결과입니다.
help()함수의 결과에서 설명한 __add__ 메소드의 사용방법대로 파라미터로 넣어준 값을 기존의 items 객체에 더하기 연산을 하여 items 리스트에 [77,88] 이라는 값이 추가된 것을 확인할 수 있습니다.
자, 지금부터는 클래스에 대하여 알아보도록 하겠습니다. 지금까지 사용한 리스트형, 튜플형, 정수형, 문자열형 등의 객체들은 각각의 객체를 정의하는 클래스들이 파이썬에 내장되어 있습니다. 그러므로 우리가 저장 공간을 명시하고 데이터를 저장하면 해당 데이터에 맞는 객체가 생성되어, 해당 객체의 타입에 맞는 메소드들이 자동으로 내장되는 것입니다.
하지만 C언어에서 구조체를 만들어 사용하는 것 처럼, 프로그래머의 요구사항을 충족하는 자료형이 필요할 경우가 있습니다. 이런 경우에 우리는 클래스를 이용하여 프로그래머가 원하는 새로운 타입의 객체를 정의할 수 있는 것입니다. 즉, 클래스를 이용하여 객체를 찍어낼 틀을 프로그래머의 입맛에 맞게 만들 수 있다는 것입니다.
위의 예는 새로운 클래스를 선언하는 내용입니다. 각각에 대한 설명을 하도록 하겠습니다.
class Stack(object):
클래스를 선언하는 부분입니다. 클래스를 선언한다는 의미로 class 라는 키워드를 적고 이어서 정의할 클래스의 이름을 명시합니다. 그리고 괄호안에는 상속 클래스명을 적습니다. 여기서는 object 클래스를 상속받았네요. 상속이라는 것은 간단히 말해 다른 클래스가 가지는 특징을 이어 받는 것입니다.
잠시 상속에 대한 설명을 하고 넘어가도록 하겠습니다.
일반적으로 상속에서 상속을 해주는 클래스를 부모 클래스, 상속을 받는 클래스를 자식 클래스라고 합니다. 이런 부모 자식 관계에서 상속을 받는 자식 클래스는 부모 클래스의 구조를 그대로 가지고 있습니다. 거기에 자신만의 요소를 더한 것입니다. 위의 예에서는 남자라는 클래스와 여자라는 클래스는 사람이라는 클래스로부터 상속을 받았기 때문에 머리, 팔, 다리, 몸통을 따로 정의하지 않더라도 해당 요소들을 가지고 있는 것입니다. 상속은 객체에서 여러가지로 쓰일 일이 많을 것이기 때문에 그 개념을 확실히 해두는 것이 좋습니다.
정리 63) 상속 : 클래스를 정의할 때, 다른 클래스의 기능 및 구조를 상속받아 사용할 수 있습니다.
본론으로 들어와서 클래스를 선언할 때, 어떤 클래스로부터도 상속을 받지 않는 경우에는 괄호에 해당하는 부분을 생략하고, 'class 클래스명:'과 같은 형태로 클래스를 선언합니다.
클래스를 선언하고 난 다음에는 클래스를 구성할 요소들을 정의해줍니다. 여기에는 데이터를 저장할 공간이나 해당 클래스에서 제공할 메소드를 정의합니다. 이번 예제의 경우에는 메소드들만 정의되어있습니다.
def __init__(self):
self.stack = []
__init__ 메소드는 조금 특별한 메소드입니다. 바로 정의된 클래스의 객체를 생성할 때에 동작하는 메소드입니다. 다른말로 생성자라고도 합니다. 생성자를 선언하는데 파라미터로 self라는 값이 보입니다.
정리 64) __init__() : 클래스 내부에 있는 메소드로, 객체를 생성할 때 동작하는 함수입니다.
이 self는 객체 자신을 가리키는 것으로, 자기 자신이 포함하고 있는 저장 공간이나, 메소드에 접근할 때 self를 사용합니다. 해당 메소드의 동작은 Stack이라는 객체가 생성될 때, 객체 자신의 내부에 stack이라는 비어있는 리스트 공간을 만듭니다.
def push(self,object):
self.stack.append(object)
이번에는 push()라는 메소드로 파라미터로 stack이라는 공간에 삽입할 데이터 혹은 객체를 받습니다. 그리고 해당 값을 append()함수를 이용하여 stack이라는 리스트에 이어붙입니다. 우리가 알고있는 개념인 스택의 푸쉬기능을 구현한 것입니다.
def pop(self):
return self.stack.pop()
이번에는 pop()이라는 메소드로, 마찬가지로 스택의 팝기능을 구현한 것이며 객체 내부에 있는 stack이라는 리스트의 마지막 값을 반환합니다.
def length(self):
return len(self.stack)
마지막으로 객체 자신의 데이터 저장공간인 stack이라는 리스트의 길이를 구하여 반환하는 메소드 len()입니다.
한가지 더, 공통적으로 객체 내에서 정의된 메소드들은 모두 self를 파라미터로 하는 것을 볼 수 있습니다. 이는 모든 메소드들이 객체 자신이 가지고 있는 데이터 저장공간에 대한 동작을 하기때문에 자기자신을 파라미터로하여 데이터 저장공간에 접근을 하기 때문입니다.
마지막으로 정의한 클래스를 사용하는 예제를 보도록 하겠습니다.
간단하게 설명하면 생성한 클래스의 객체를 생성하고, 객체의 메소드인 push()를 이용하여 값들을 넣고, pop() 메소드를 이용하여 값을 빼내고 마지막으로 객체를 삭제하는 순입니다.
>>> s = Stack()
프로그래머가 정의한 Stack() 클래스의 객체 s 를 생성합니다.
이 과정은 클래스의 객체를 생성하는 과정이므로 Stack 클래스의 생성자인 __init__() 메소드가 호출되어 self라는 이름으로 객체의 실체를 만듭니다. self가 생기면서, 클래스에 정의된 객체의 형태가 실체를 갖게됩니다. 이어 self라는 객체의 실체 내부에 stack이라는 이름의 비어있는 리스트를 만듭니다.
이어서 __init__() 메소드로 만든 객체의 실체 self 내부에 있는 stack이라는 리스트의 링크는 s에 연결됩니다.
결과적으로 데이터를 저장할 리스트 공간은 객체 내부에서는 self.stack이라는 이름을 가지고, 외부에서는 s 라는 이름을 가지게 되는 것입니다.
>>> s.push("haerakai")
다음은 push() 메소드의 파라미터로 haerakai라는 문자열을 주었습니다.
이 값은 push() 메소드의 object라는 파라미터에 저장이 되었습니다.
이어 push() 메소드의 파라미터 object에 저장된 haerakai라는 문자열은 push() 메소드 내부에 있는 append() 명령을 통해 객체의 데이터 저장공간인 stack에 추가됩니다. 여기서 사용된 append() 함수는 클래스 Stack이 상속받은 클래스인 object 클래스에 있는 함수입니다. 따라서 object 클래스의 자식 클래스인 Stack 클래스에서도 함수를 사용할 수 있는 것입니다.
위와 같은 원리로 s.push(55) 명령과 s.push([77,88,99]) 명령을 수행하고 나면 다음과 같은 상태가 됩니다.
각각의 push() 명령을 통해 추가한 값들이 객체의 실체 s에 저장된 것을 확인할 수 있습니다.
>>> x = s.pop()
이번에는 객체의 실체인 s(==self.stack)에 저장되어 있는 값을 꺼내는 메소드인 pop()를 사용하였습니다. 그리고 반환되는 값을 x라는 공간에 저장하도록 합니다.
Stack 클래스의 객체가 가진 pop() 메소드 역시 object 클래스로부터 상속받은 pop() 함수를 사용할 수 있습니다. 따라서, s 객체의 pop() 메소드를 호출하면 위와 같이 object 클래스로 부터 상속받은 pop() 함수를 이용하여 self.stack의 가장 마지막에 있는 값을 꺼내고, 이 값을 반환합니다.
위의 그림을 통해 객체의 pop() 메소드로부터 반환된 값이 x에 저장되었음을 확인할 수 있습니다.
다음 명령어인 y = s.pop() 역시 같은 원리로 동작을 하고 결과적으로 다음과 같은 상태가 됩니다.
여기서는 y에 정수형 데이터가 직접 저장된 것으로 나오네요. 이부분은 확인하여 다시 작성하도록 하겠습니다.
>>> del s
마지막으로 del 명령은 필요없는 객체를 삭제하는 동작을 합니다.
del s 명령을 사용한 결과로 s 객체가 사라진 것을 확인할 수 있습니다.
정리 65) del s : 필요없는 객체를 삭제하는 명령입니다.
이렇게 파이썬에서의 객체 그리고 클래스에 대하여 알아보았습니다.
정리모음정리 59) 클래스와 객체 : 클래스는 객체의 틀이 되는 추상적인 개념이고, 객체는 클래스에 정의된 요소들의 실체입니다.정리 60) 객체의 구조 : 객체는 내부에 데이터를 저장할 공간과 해당 데이터에 관련된 메소드들을 가지고 있습니다.정리 61) dir(items) : 파라미터로 주어진 객체가 가지고 있는 메소드들의 이름을 보여줍니다.정리 62) help(items) : 객체가 저장하는 데이터의 타입과 메소드에 대한 자세한 설명을 보여줍니다.정리 63) 상속 : 클래스를 정의할 때, 다른 클래스의 기능 및 구조를 상속받아 사용할 수 있습니다.정리 64) __init__() : 클래스 내부에 있는 메소드로, 객체를 생성할 때 동작하는 함수입니다.정리 65) del s : 필요없는 객체를 삭제하는 명령입니다.참고 URL 및 도서
- 파이썬 완벽 가이드, 데이비드 M. 비즐리 저, 2012년, 인사이트 펴냄
'Python > study' 카테고리의 다른 글
파이썬의 모듈 (0) 2013.12.18 파이썬의 예외처리 (0) 2013.12.18 파이썬의 코루틴 (2) 2013.12.18 파이썬의 제네레이터(추가) (0) 2013.12.18 파이썬의 제네레이터와 이더레이터 (8) 2013.12.18 파이썬의 함수 (0) 2013.12.18 파이썬의 반복문 (0) 2013.12.18 파이썬의 파일 입출력 (0) 2013.12.18 파이썬의 조건문 (0) 2013.12.18 파이썬의 자료형 - 사전 (0) 2013.12.18