python 2

본 토픽은 현재 준비중입니다. 공동공부에 참여하시면 완성 되었을 때 알려드립니다.

객체지향 프로그래밍 1

객체지향 프로그래밍

객체지향 프로그래밍(Object-Oriented Programming)은 좀 더 나은 프로그램을 만들기 위한 프로그래밍 패러다임으로 로직을 상태(state)와 행위(behave)로 이루어진 객체로 만드는 것이다. 이 객체들을 마치 레고블럭처럼 조립해서 하나의 프로그램을 만드는 것이 객체지향 프로그래밍이라고 할 수 있다. 다시말해서 객체지향 프로그래밍은 객체를 만드는 것이다. 따라서 객체지향 프로그래밍의 시작은 객체란 무엇인가를 이해하는 것이라고 할 수 있다.

객체지향 프로그래밍을 학습하는데 장애 중의 하나는 번역이다. Object를 번역한 객체는 현실에서는 거의 쓰지 않는 말이고, 머랄까 철학적인 느낌을 자아낸다. 그래서 객체지향 프로그래밍을 처음 접하는 입문자들은 객체지향 프로그래밍을 철학적인 탐구의 대상으로 파악하는 경향을 보이는데, 필자의 생각에 이것은 공부를 어렵게 할 뿐 도움이 되지 않는다. 쉽게 생각하자. 객체는 변수와 함수를 그룹핑한 것이다.

객체가 없다면

하나의 프로그램은 변수와 함수로 이루어져있다. 변수에 값을 담고, 연산을 함수로 묶는다. 그리고 이것들을 하나씩 실행하면서 프로그램이 동작하게 된다. 아래의 코드는 우항과 좌항을 더한 결과를 출력하는 매우 간단한 프로그램이다. 두개의 변수와 하나의 함수가 있고, 함수 sum을 호출하고 있다. 결과는 3이다.

left = 1
right = 2


def sum():
    global left, right
    return left + right

print sum()

이 정도 규모의 코드에서는 나쁜일이 일어날 가능성이 거의 없다. 하지만 프로그램은 점점 커지고 그에 따라 더 많은 사람이 코드의 작성에 참여하게 된다. 프로그램의 규모가 커지기 시작하면서 위의 코드가 약 1만줄에 이르는 방대한 코드가 되었다고 간주해보자. 코드의 복잡도는 기하급수적으로 늘어날 것이고 다양한 어려움에 봉착하게 될 것이다.

예를들어, 새로 합류한 개발자가 사용자의 적립금을 출력하는 로직을 작성하기 위해서 로직을 아래와 같이 고쳤다고 해보자.

이 예제는 프로그램으로써 좋은 예제는 아니다. 서로 다른 개발자가 right의 의미를 다르게 사용했을 때 발생할 수 있는 문제를 위한 인위적인 예제다
left = 1
right = 2


def sum():
    global left, right
    return left + right


right = 10000


def my():
    global right
    return right

print sum()     #10001
print my()	#10000

함수 my의 실행결과(10000)는 정상적으로 출력 되지만 함수 sum은 엉뚱한 값(10001)을 출력하고 있다. 이것은 나중에 합류한 개발자에 의해서 right의 의미가 계산식의 오른쪽항에서 사용자의 권리에 해당하는 금액으로  달라졌기 때문이다. 프로그램의 규모가 커지고 개발자가 많아질수록 이런 일이 빈번해질 것이다. 즉 서로 관련이 없는 로직인 sum과 my가 하나의 프로그램 안에서 동작하고 있기 때문에 서로에게 영향을 끼치고 있는 것이다.

객체화

객체지향 프로그래밍은 입문자에게는 다소 어려운 토픽이고, 객체지향이 무엇인가를 모른다고 프로그램을 만들지 못하는 것은 아니다. 따라서 객체지향 프로그램을 이해하는데 어려움이 있다면 객체지향적인 기법 없이 코드를 작성하면 된다. 그러다보면 차차로 다양한 객체들을 만나게 될 것이고 객체지향에 대해서 이해하기 좋은 상태가 될 것이다. 그 때 다시 도전해도 된다. 하지만 이 언어를 깊게 이해하고, 더 고도화된 프로그램을 만들기 위해서는 꼭 짚고 넘어가야 할 주제라는 점도 잊지 말자.

그럼 어떻게 해야할까? 이해를 돕기 위해서 비유를 들어보자. 컴퓨터에서 데이터를 보관하는 기본 단위는 파일이다. 파일이 있기 때문에 저장장치(하드디스크, SSD) 전체를 하나로 사용하는 것이 아니라 내용에 따라서 파일 단위로 쪼개서 사용할 수 있다. 마찬가지로 파일도 많아지면 관리의 어려움이 생긴다. 이것을 위해서 고안된 방법이 디렉토리(폴더)다. 즉 디렉토리를 이용해서 연관된 파일들을 그룹핑 함으로써 관리의 편의를 도모하게 되는 것이다.

객체란 디렉토리와 비슷한 목적으로 고안된 것이라고 생각하자. 연관되어 있는 변수와 메소드를 그룹핑해서 관리의 편의를 도모하는 것이 객체를 사용하는 이유다. (물론 훨씬 더 많은 이유가 있다)

다음 예제는 이전 예제를 객체화 시킨 것이다. 이 예제를 통해서 객체가 무엇인가를 알아보자.

class Calculator:

    def __init__(self, left, right):
        self._left = left
        self._right = right

    def sum(self):
        return self._left + self._right


class User:

    def __init__(self, right):
        self._right = right

    def my(self):
        return self._right

c = Calculator(1, 2)
print c.sum()

u = User(10000)
print u.my()

클래스와 인스턴스

클래스란 객체의 설계도이고 인스턴스는 그 설계도에 따라서 만들어진 구체적인 제품이라고 비유 할 수 있다. 이 말의 의미를 실제 코드를 통해서 알아보자.

아래의 코드는 Calculator, User라는 이름의 클래스를 만들기 위한 것이다. 이 클래스는 객체 Calculator과 User에 대한 일종의 설계도라고 할 수 있다.

class Calculator:
class User:

익숙한 함수로 비유를 해보자. 함수도 설계도가 있다. 바로 def라는 키워드다. 함수의 설계도는 아래와 같은 형식을 가지고 있다.

def 함수명:

위의 내용은 함수를 정의 하는 것일 뿐 실제로 함수의 로직을 실행하기 위해서는 아래와 같이 함수를 호출해야 한다.

함수명()

객체도 마찬가지다. 객체를 실제로 사용하기 위해서는 아래와 같이 객체의 생성을 컴퓨터에게 지시해야 한다.

s = Calculator(1,2)

위의 코드는 변수 s에 객체 Calculator을 담아서 저장한다는 의미다. 위의 구문이 실행되면 변수 s를 이용해서 Calculator 객체를 사용할 수 있게 된다. 이렇게 생성된 객체를 인스턴스(instance)라고 부른다. 즉 객체의 설계도를 클래스라고 부르고, 이 설계도를 바탕으로 실제로 사용할 수 있게 만들어진 객체를 인스턴스라고 한다. 이것들을 포괄해서 객체라고 부르기도 한다. 객체라는 말의 의미는 맥락적으로 파악해야 한다.

앞으로 필자는 설계도를 클래스, 만들어진 객체를 인스턴스라고 구분해서 부르겠다.

생성자

Calculator()은 객체를 생성할 때 사용하는 규칙이다. 클래스의 이름()이 호출되면 클래스 내의 def __init__ 함수가 실행된다. 이 메소드를 생성자(constructor)라고 부른다. 함수 __init__은 객체의 초기화를 위한 특수한 함수로 이 이름으로 클래스 내에 함수를 정의하면 클래스가 인스턴스화 될 때 실행된다. 이것은 최초로 한번 실행되기 때문에 뒤에서 배울 인스턴스 변수의 초기 값을 셋팅하거나, 데이터베이스 접속을 시도하는 것과 같이 기본적으로 필요한 작업을 할 때 사용된다.

생성자는 인자를 가질 수 있다. 위의 예제에서는 Calculator(1, 2)를 통해서 1과 2를 인자로 전달했다. 이것은 아래 Calculator 클래스 내의 아래 코드로 인해서 인자변수 left의 값으로 1, right의 값으로 2가 전달된다. 이 변수들의 값은 각각 변수 self._left, self._right에 담겨진다. 그럼 다음 절에서 'self'가 무엇인지 알아보자.

def __init__(self, left, right):
    self._left = left
    self._right = right

인스턴스 변수와 메소드

파이썬에서는 객체 안에서 정의된 함수를 메소드(method)라고 부른다. 그런데 파이썬의 메소드는 함수와는 조금 다른 특징이 있다. 위의 예제에서 정의한 메소드 __init__은 인자가 3개다. 아래 그림을 보자.

인스턴스가 생성될 때 첫번째 인자 1은 메소드 __init__의 두번째 인자의 값으로 전달 되었다. 좀 이상하다. 그럼 self는 무엇일까? 파이썬에서 메소드의 첫번째 인자는 그 메소드가 소속된 인스턴스를 가르키는 값이 들어가도록 약속되어 있다. 따라서 self._left는 인스턴스에 속해있는 변수라는 의미다.

첫번째 인자의 이름은 self가 아니라 다른 이름을 사용해도 된다. 하지만 관습적으로 self를 쓴다.
필자는 함수와 메소드를 구분해서 부르겠다.

메소드의 첫번째 인자(위의 코드에서는 self) 뒤에 .(점)을 붙여 따라오는 변수를 인스턴스 변수(instance variable)라고 부른다. 인스턴스에 소속된 변수라는 것이다. 위의 예제에서 left, right는 메소드 안에서만 유효한 지역변수고 self_.left, self._right는 인스턴스에서 소속된 인스턴스 변수이다. 다시말해서, 인스턴스 변수인 self._left, self._right는 인스턴스에 소속되어 있기 때문에 인스턴스를 통해서만 접근이 가능하다는 의미다. 덕분에 첫번째 예제 처럼 right의 의미를 다르게 사용해도 right가 속해 있는 객체가 다르기 때문에 문제가 발생할 확률이 현저하게 낮아진다.

python은 java와 같은 언어들과는 다르게 변수를 인스턴스 밖에서 보이지 않도록 하는 private 기능이 사실상 존재하지 않는다. 변수의 이름 앞에 _(언더바)를 두개 붙이면 외부의 객체에서 접근 할 수 없게 할 수 있지만 이것 역시 엄격한 의미로는 private 기능이 아니다. 본 내용은 이해하지 못해도 된다.

상태와 행위

앞에서 객체를 '연관된 상태와 행위의 그룹'이라고 정의했다. 그리고 상태를 변수, 행위를 메소드라고 생각하자고 했다. 이 말의 의미를 곱씹어보면서 아래를 보자.

c1 = Calculator(1, 2)
c2 = Calculator(3, 4)

두개의 인스턴스를 만들었다. 각각의 인스턴스는 생성자로 인자1과 인자2가 전달 됐다. 인스턴스 c1의 상태는 아래와 같다.

  • self._left : 1
  • self._right : 2

아래는 c2의 상태다.

  • self._left : 3
  • self._left : 4

인스턴스 c1과 c2는 서로 다른 상태를 가지고 있는 것이다. 즉 인스턴스 변수의 값이 그 객체의 '상태(state)'가 되고, 메소드는 그 상태를 참고해서 '행위(behave)'를 하게 되는 것이다. 그 결과 c1.sum의 결과는 3이 되고, c2.sum의 결과는 7이 된다. 객체에 대해서 다시 정리해보자.

객체화란

  1. 설계도인 클래스를 바탕으로
  2. 서로 다른 상태(변수)에 따라 
  3. 동일한 행동(메소드)을 하는
  4. 구체적인 제품인 인스턴스를
  5. 사용한 것이다.
상태나 행위라는 표현은 기억하지 않아도 된다. 중요한 것은 객체가 무엇인가를 이해하는 것이다.

마치며

이번 토픽에서는 객체가 무엇인가를 설명하기 위해서 많은 노력을 들였다. 하지만 객체지향 프로그래밍은 책 한권이 별도로 존재할 수 있을 정도로 그 내용이 깊고 방대하다. 이것을 다 알면 좋겠지만 그건 차차하면 된다. 이번 토픽에서 소개된 객체지향의 개념만 알고 있어도 Python에서 프로그래밍을 하는데 큰 도움이 될 것이다. 객체지향에 대한 더 많은 내용은 후속 강의를 통해서 다루기로 하겠다.

  • 봤어요 (0명)

댓글

댓글 본문
  1. 객체 지향에 관해 감이 안 잡혔는데 덕분에 감이 조금 잡힌거 같아요. 감사합니다!
  2. 이수진
    https://highlightjs.org/ 여기서 코드 복+붙하시고 테마 선택하시면 됩니다.
    대화보기
    • egoing
      syntax highlight라는 자바스크립트 라이브러리의 기능입니다. opentutorials.org에서는 기능으로 제공하고 있구요. http://alexgorbatchev.com......er/
      대화보기
      • egoing
        아래 내용을 참고해주세요.

        http://kldp.org......203
        대화보기
        • 육수
          python은 java와 같은 언어들과는 다르게 변수를 인스턴스 밖에서 보이지 않도록 하는 private 기능이 사실상 존재하지 않는다. 변수의 이름 앞에 _(언더바)를 두개 붙이면 외부의 객체에서 접근 할 수 없게 할 수 있지만 이것 역시 엄격한 의미로는 private 기능이 아니다. 본 내용은 이해하지 못해도 된다.


          -- 만약에 이해를 하고 싶다면, 어떤 예제를 보면서 비교하면 쉬울까요/?
        • 육수
          이고잉님 질문 있습니다.!
          코드를 첨부할 때 저렇게 줄이 나오게 하는 거는 어떻게 하신건가요? 어떤 코드를 쓰면 저런 효과(?)가 나는지 궁금합니다.
        • egoing
          제기 기획한 언어 수업의 특성은 공통적인 것들이 모든 수업에 분산되어 있어서 어느 언어로 집입해도 문제가 없도록 하는 것이 목표입니다. 그래서 지금 파이썬을 공부하시면 루비를 공부할 때 공통적인 것은 넘어가면서 루비라는 언어의 특성에 집중하실 수 있을 것 같습니다. 아래 두개의 수업을 비교해보시면 내용이 거의 똑같은 것을 확인하실 수 있을꺼예요. http://opentutorials.org/cours... http://opentutorials.org/cours...
          2013년 6월 30일 오후 5:09, Disqus <notifications@disqus.net>님의 말:</notifications@disqus.net>
          대화보기
          • Sukmin Lim
            아... 정리하려니 고민이군요.. 역시 머리속에 내용을 말로 꺼내는 일은 참으로 어렵습니다 ㅠㅠ 객체지향을 지닌 언어들은 객체지향이 추구하는 일반적인 속성들을 가지고 있는데 그걸 제가 언어 공통부분이라고 정의 내렸습니다(..)
            언어 수업이 생겼기 때문에, 언어의 공통된 속성을 모아서 한번에 공부하는 것이 조금더 효율적이지 않을 까 라는 생각에 적어봤습니다!
            대화보기
            • egoing
              언어공통부분이란 표현을 이해하지 못했습니다. 부가설명 부탁해요~
              2013년 6월 30일 일요일에 Disqus님이 작성:
              대화보기
              • Sukmin Lim
                객체지향은 일반화 시켜서 언어 공통부분에 넣는게 좋지 않을까란 생각과 객체지향을 공부하는것이라면, 캡슐화와 상속, 다형성까지 설명해주시고, 절차지향과 비교하는 글을 써주신다면 조금더 이해가 쉬워..질까요? ㅠㅠ 쓰고나니 확신이 서지 않습니다. 저 같은 경우에는 C를 기반으로 먼저 컴퓨터 언어를 접했는지라 ㅠㅠㅠ
              버전 관리
              egoing
              현재 버전
              선택 버전
              graphittie 자세히 보기