Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

Contents

유저 정의 타입

그동안 다양한 파이선 내장 타입을 사용해왔다. 이제 새로운 타입을 만들 차례다. 2차원 공간에 점을 표시하는 Point 라는 유형을 만들어보자.

수학에서 점의 위치는 주로 좌표로 나타낸다. 예를 들어 (0,0)은 원점을 나타내며 (x,y)는 원점에서 오른쪽으로 x, 위쪽으로 y의 위치에 있다는 것을 나타낸다.

파이선에서 point를 표현하는 몇 가지 방법들이 있다.
  1. 좌표를 두개의 변수 x, y에 저장 한다.
  2. 좌표를 리스트 혹은 튜플로 저장한다.
  3. 좌표 정보를 표현하기 위한 새 타입을 만든다.
새로운 타입을 만드는 것은 다른 옵션보다는 (조금)복잡하다. 그러나 장점도 가지고 있다.

유저가 정의하는 이런 타입을 class(클래스)라고 부른다. class는 아래와 같이 정의 한다.
class Point(object):
    """Represents a point in 2-D space."""
1번째 줄은 Point라는 이름을 가지는 새로운 클래스 타입이라는 것을 알려준다. 그리고 2번째 줄에 클래스에 대한 설명을 넣었다. 클래스는 변수와 함수를 함께 정의 할 수 있는데, 나중에 살펴보도록 하겠다.

클래스 Point를 정의 하면 class 객체가 만들어진다.
>>> print Point
<class '__main__.Point'>
Point는 탑레벨에서 정의됐으므로 풀네임은 __main__.Point 가 된다.

이제 Point 클래스로 부터 새로운 객체를 만들 수 있다. 함수를 실행하는 것과 비슷한 방법으로 객체를 만들 수 있다.
>>> blank = Point()
>>> print blank
<__main__.Point object at 0x7fd4278cc190>
반환 값인 blank는 Point를 가리킨다(reference). 이렇게 새로만들어진 객체를 인스턴스(instance)라고 부른다.

이 인스턴스를 print 하면 파이선은 인스턴스가 어느 클래스로 부터 만들어졌는지와 저장된 메모리의 위치를 알려준다.

Attributes

.를 이용해서 인스턴스에 값을 할당 할 수 있다.
>>> blank.x = 3.0
>>> blank.y = 4.0
문법은 math.pistring.whitespace처럼 모듈로 부터 변수를 선택하는 것과 비슷하다. 이 경우 우리는 객체가 가지고 있는 엘리먼트에 값을 할당하게 되는데, 이 엘리먼트를 어트리뷰트(attributes)라고 부른다.

아래 다이어그램은 어트리뷰트에 값을 할당한 결과를 보여준다. 객체와 그것의 어트리뷰트를 보여주는 다이어그램을 객체다이어그램 이라고 부른다.

 파이선 객체 다이어그램

변수 blank는 두 개의 어트리뷰트를 가지고 있는 Point 객체를 가리킨다. 각 어트리뷰트는 floating-point 숫자를 가리키고 있다.

아래와 같이 어트리뷰터에 저장된 값을 읽을 수 있다.
>>> print '(%g, %g)' % (blank.x, blank.y)
(3.0, 4.0)
>>> distance = math.sqrt(blank.x**2 + blank.y**2)
>>> print distance
5.0
또한 다른 함수의 매개변수로 넘길 수도 있다.

Rectangles

클래스는 객체(사물)의 청사진 역할을 한다. 제대로된 청사진을 만들려면 설계가 필요하며, 우리는 객체가 어떤 속성을 가지고 있는지 그 속성들을 어떻게 제어할지를 결정해야 한다. Rectangles(사각형)을 표현하기 위한 클래스를 만든다고 가정해보자. 사각형의 위치와 크기를 나타내기 위해서는 어떤 속성들이 있어야 할까 ? 문제를 단순화하기 위해서 각도는 무시하자. 정사각형 혹은 직사각형만 표현 할 수 있으면 된다.

Rectangles 클래스는 적어도 아래의 속성들을 가지고 있어야 할 것이다.
  • 사각형의 꼭지점(혹은 중앙)에서의 높이(height)와 폭(width)
  • 사각형 위치를 나타내기 하변 꼭지점의 x,y 값
사각형의 위치 속성은 앞서 정의한 Point클래스로 선언할 수 있을 것이다. 클래스를 만들어보자.
class Point(object):
    """Point
        attributes: x, y
    """
class Rectangle(object):
     """Represents a rectangle.

     attributes: width, height, corner.
     """

box = Rectangle()
box.width = 100.0
box.height = 200.0
box.corner = Point()
box.corner.x = 0.0
box.corner.y = 0.0 
box.corner.x 는 box 객체가 가지고 있는 corner 속성을 선택하고, corner의 x 속성을 선택하라는 의미다. 아래의 클래스 다이어그램을 보자.

 Rectangle 다이어그램

box 객체는 Point라는 외부 객체를 가지고 있는데, 이를 embedded객체라고 부른다.

인스턴스와 반환 값

함수는 인스턴스를 반환할 수 있다. 예를 들어 find_center는 Rectangle 객체를 매개변수로 받아서 Rectangle의 위치 값을 가진 Point 객체를 반환 할 수 있다.
def find_center(rect):
    p = Point()
    p.x = rect.corner.x + rect.width/2.0
    p.y = rect.corner.y + rect.height/2.0
    return p
아래 예제는 find_center 함수를 이용해서 Point 객체를 반환 받고, 이 객체를 print_point 함수로 출력하고 있다.
>>> center = find_center(box)
>>> print_point(center)
(50.0, 100.0)

객체는 mutable 하다.

당신은 객체의 속성 중 하나를 할당해서 객체의 상태를 변경 할 수 있다. 예를 들어 직사각형의 위치는 그대로 두고, height와 wight 값을 변경해서 직사각형의 크기를 변경 할 수 있다.
box.width = box.width+50
box.height = box.height+100
또한 함수에서도 객체를 수정할 수 있다. 예를 들어 grow_rectangle는 Rectangle객체가 가지고 있는 dwidth와 dheight의 값을 수정한다.
def grow_rectangle(rect, dwidth, dheight):
    rect.width += dwidth
    rect.height += dheight
테스트 해보자.
>>> print box.width
100.0
>>> print box.height
200.0
>>> grow_rectangle(box, 50, 100)
>>> print box.width
150.0
>>> print box.height
300.0
grow_rectangle 함수에서 rect는 box의 alias 이다. 따라서 함수에서 rect를 수정하는 것은 box를 수정하는 것과 같다.

복사

Aliasing은 한쪽에서 값이 바뀔 경우 다른 영역에서의 값에도 영향을 주기 때문에, 프로그램 코드를 읽기 어렵게 만들며 원하지 않는 부수효과를 가져올 수 있다.

Aliasing의 대안으로 copying을 쓸 수 있다. copy 모듈의 copy 함수를 이용해서 객체를 복사 할 수 있다.
>>> p1 = Point()
>>> p1.x = 3.0
>>> p1.y = 4.0

>>> import copy
>>> p2 = copy.copy(p1)
p1과 p2는 같은 데이터를 가지고 있지만 다른 객체다.
>>> print_point(p1)
(3.0, 4.0)
>>> print_point(p2)
(3.0, 4.0)
>>> p1 is p2
False
>>> p1 == p2
False
copy.copy로 Rectangle를 복사해보자. 복사된 Rectangle 객체는 원본과 다른 객체이지만 임베디드된 Point 객체는 같은 객체를 참조한다.
>>> box2 = copy.copy(box)
>>> box2 is box
False
>>> box2.corner is box.corner
True
아래 그림을 보자.

 임베디드 객체를 포함한 객체의 복사

Rectangle 객체를 복사한 모습을 보여주고 있다. 객체가 참조하는 모든 데이터를 복사하지만 임베디드된 객체는 제외하기 때문에 얕은 복사(shallow copy)라고 부른다.

대부분의 애플리케이션에서, 이런 작동을 원하지 않을 것이다. grow_rectangle를 호출하면 다른 객체에는 영향을 미치지 않지만 move_rectangle를 호출하면, 모든 객체에 영향을 미친다. 이 동작은 매우 혼란스럽고 오류 발생 확률을 높인다.이 경우 임베디드된 객체까지 모두 복사하는 deepcopy라는 메서드를 이용하면 된다.