본문 바로가기

Python/자료구조 (Data Structure)

5-1. 클래스와 객체 (Class and Object): 파이썬 자료구조와 알고리즘

 

프로그래밍에서 객체란 흔히 붕어빵과 붕어빵틀로 비유하는, 유사한 속성들을 가진 물체들을 클래스라는 사전 정의로 정의한 후 사용하는 것들을 뜻하는 용어이다.

클래스와 객체

클래스 (class) 란, 사전에 정의된 특별한 데이터와 메서드의 집합이다. 클래스에 선언된 모양 크대로 생성된 실체를 객체 (object) 라고 한다. 그리고 그 객체가 소프트웨어에 실체화될 때 (즉, 메모리에 할당되어 사용될 때), 이를 인스턴스 (instance) 라고 한다. 객체는 인스턴스를 포함하며, 포괄적인 의미를 가진다. 파이썬에서 가장 간단한 클래스는 다음과 같이 만들 수 있다.

>>> class BoongEoBbang:
    pass

>>> x = BoongEoBbang()
>>> x
<__main__.BoongEoBbang object at 0x0301B2F8>

클래스 인스턴스 생성

클래스 인스턴스 생성 (class instantiation) 은 함수 표기법을 사용하여 초기 상태의 객체를 생성하는 일이다. 인스턴스 생성 작업은 어떤 특징을 가진 빈 객체를 만드는 것이다. 여러 이름을 같은 객체에 바인딩 (binding) (또는 앨리어싱, aliasing) 할 수 있다. 가령, Hello라는 클래스가 있다고 하면, Hello() 를 통해 호출하여 객체를 생성하는데, 이 때 Hello() 를 생성자 (constructor) 라고 한다. 생성자를 호출하면, Hello.__new__() 라는 특수 메서드의 호출에 따라 객체가 할당되고 그 후 Hello.__init__() 메서드가 객체를 초기화한다.

속성

객체에는 데이터 (data) 와 메서드 (method) 로 이루어지는 클래스 속성 (attribute) 이 있다. 메서드 속성은 함수이며, 그 첫 번째 인수는 호출된 인스턴스 자신이다 (이를 파이썬에선 셀프, self 라고 한다). 속성은 점 (.) 뒤에 나오는 모든 이름이다. 모듈 내 모든 이름의 참조는 속성 참조이다. 즉 모듈명.함수명과 같은 표현식에서 모듈명은 모듈 객체이고, 함수명은 객체 클래스 속성이다. 속성은 읽기 전용일 수도, 쓰기 가능할 수도 있으며, 쓰기 가능한 속성은 del 문으로 삭제 가능하다.

네임스페이스

네임스페이스 (namespace) 는 이름을 객체로 매핑 (mapping) 하는 것이다. 대부분의 네임스페이스는 파이썬 딕셔너리로 구현되어있다. 네임스페이스의 예로는, 내장된 이름 셋, 모듈의 전역이름, 함수의 지역이름 등이 있다. 스크립트 파일 또는 IDLE의 최상위 호출에 의해 실행되는 명령문은 __main__ 이라는 모듈의 일부로 간주되어 고유의 전역 네임스페이스를 갖는다.

스코프

스코프 (scope) 는 네임스페이스에 접근할 수 있는 파이썬 프로그램의 텍스트 영억 (textual region) 이다. 스코프는 텍스트에 따라 결정된다. 즉 한 모듈에 정의된 함수의 전역 스코프는 해당 모듈의 네임스페이스이며, 클래스 정의가 실행되면 새로운 네임스페이스가 만들어져 지역 스코프로 사용된다.

객체지향 프로그래밍의 원리

특수화

특수화 (specialisation) 는 슈퍼 (super) 클래스 (부모, parent 또는 베이스, base 클래스라고도 한다) 의 모든 속성을 상속 (inheritance) 하여 새 클래스를 만드는 절차이다. 모든 메서드는 서브클래스 (subclass) 또는 자식 (child) 클래스에서 재정의 (override) 또는 재구현 (re-implemented) 될 수 있다. (파이썬에서 모든 메서드는 가상, virtual 이다). 상속은 is-a 관계이다. 이게 무슨말이냐면, 예를들어 사람이라는 클래스를 만들었고 이를 개발자라는 클래스에 상속시켰다고 가정하자. 그러면 모든 개발자는 사람이다 (developer is-a human). 그러나, 모든 사람이 개발자는 아닌 것과 같은 것이다.

구글 파이썬 스타일 가이드는 한 클래스가 다른 클래스를 상속 받지 않을 때, 파이썬의 최상위 클래스인 object를 명시적으로 표기하는 것을 권장한다 (아래와 같이).

>>> class SampleClass(object):
    pass

>>> class OuterClass(object):
    class InnerClass(object):
        pass

다형성

다형성 (polhymorphism) 또는 동적 메서드 바인딩은 메서드가 서브 클래스 내에서 재정의될 수 있다는 원리이다. 즉 서브 클래스에서 슈퍼 클래스와 동명의 메서드를 호출하면, 파이썬은 자동적으로 서브 클래스에서 재정의된 메서드를 사용한다는 것이다. 만약 슈퍼 클래스의 메서드를 호출해야 한다면 내장된 super() 메서드를 사용해 이를 호출할 수 있다.

예를 들어, 파이썬에서 사용자 정의 클래스의 모든 객체는 기본적으로 해시 가능 (hashable) 하다. 객체가 해시 가능하다는 것은 hash() 속성을 호출할 수 있다는 뜻이며, 불변 객체임을 의미한다.

>>> class Symbol(object):
    def __init__(self, value):
        self.value = value


>>> x = Symbol("Py")
>>> y = Symbol("Py")
>>> symbols = set()
>>> symbols.add(x)
>>> symbols.add(y)
>>> print(x is y)
False
>>> print(x==y)
False
>>> print(len(symbols))
2

위를 보면, 첫번째 출력은 x 와 y 가 서로 다른 참조를 하고 있으니 False로 뜨는 것이 맞다. 그러나 두번째를 보면 x와 y가 값이 같음에도 결과는 False이다. set에 들어간 x와 y도 값이 같음에도 중복항목으로 취급되지 않아 길이가 2가 나왔다.

이를 고쳐주기 위해선 객체의 비교를 담당하는 __eq__() 메서드를 재정의 해야한다. 재정의를 하고 다시 실행해 보면,

>>> class Symbol(object):
    def __init__(self, value):
        self.value = value
    def __eq__(self, other):
        if isinstance(self, other.__class__):
            return self.value == other.value
        else:
            return NotImplemented


>>> x = Symbol("Py")
>>> y = Symbol("Py")
>>> x == y
True
>>> x is y
False
>>> symbols = set()
>>> symbols.add(x)
Traceback (most recent call last):
  File "<pyshell#24>", line 1, in <module>
    symbols.add(x)
TypeError: unhashable type: 'Symbol'

보면 둘의 비교를 할 때, 비교 결과는 예상한대로 제대로 잘 나온다. 그러나 set에 넣으려하니 unhashable type TypeError가 난다. 객체가 해시 가능하지 않나는건 (unhashable), 이들이 가변 객체임을 의미한다. 에러를 고치기 위해 __hash__() 메서드를 추가하면,

>>> class Symbol(object):
    def __init__(self, value):
        self.value = value
    def __eq__(self, other):
        if isinstance(self, other.__class__):
            return self.value == other.value
        else:
            return NotImplemented
    def __hash__(self):
        return hash(self.value)


>>> x = Symbol("Py")
>>> y = Symbol("Py")
>>> x == y
True
>>> x is y
False
>>> a = set()
>>> a.add(x)
>>> a.add(y)
>>> len(a)
1

의도했던 대로 잘 되는 것을 볼 수 있다.

합성과 집합화

합성 (composition) 그리고 집합화 (aggregation) 는 한 클래스에서 다른 클래스의 인스턴스 변수를 포함하는 것을 말하며, 클래스 간의 관계를 나타낸다. 파이썬의 모든 클래스는 상속을 사용한다 (object 베이스 클래스로부터 상속 받는다). 대부분 클래스는 다양한 타입의 인스턴스 변수를 가지며, 합성과 집합화를 사용한다.

두 클래스, A와 B가 있다고 가정하자. 합성은 A와 B가 강한 연관 관계를 맺으며, 강한 생명주기 (strong lifecycle) 를 갖는다. 즉 의존성이 강하다. 예를 들어, 집 클래스는 방 클래스를 갖는다. 집이 있으면 방이 있다.

집합화는 A와 B가 연관 관계가 있지만, 생명주기가 약하며 독립적이다. 예를 들어 학생 클래스는 미술, 음악 등의 과목 클래스를 갖는다. 한 학생은 미술, 음악 두 과목을 모두 수강할 수도 있지만 하나만 수강하거나 모두 수강하지 않을 수도 있다.

출처

파이썬 자료구조와 알고리즘 - 한빛미디어, 미아 스타인