Lumpy

[lumpy]

책의 도처에서, 실행중인 프로그램의 상태를 나타내기 위해 다이어그램을 사용했습니다.

[variables] 절에서, 변수의 이름과 값을 보이기 위해 상태도(state diagram)를 사용했습니다. [stackdiagram] 절에서는 스택 다이어그램(stack diagram)을 소개했는데, 각 함수마다 하나의 프레임을 보여줍니다. 각 프레임은 함수나 메쏘드의 매개변수와 지역 변수들을 보여줍니다. 재귀적 함수들의 스택 다이어그램은 [recursive.stack] 절 과 [more.recursion] 절에 등장합니다.

[mutable] 절은 상태도에서 리스트를 나타내는 법을, [invert] 절은 딕셔너리를 나타내는 법을, [dictuple] 절은 튜플을 표현하는 두 가지 방법을 보여줍니다.

[attributes] 절은 객체 다이어그램(object diagram)을 소개하는데, 객체의 애트리뷰트들의 상태, 또 애트리뷰트들의 애트리뷰트들의 상태, ... 등을 보여줍니다. [rectangles] 절에는 Rectangle 들과 내장된 Point 들의 객체 다이어그램이 있습니다. [time.object] 절은 Time 객체의 상태를 보여줍니다. [class.attribute] 절에는 각기 그들의 애트리뷰트들과 함께 클래스 객체와 인스턴스를 포함하는 다이어그램이 있습니다.

마지막으로, [class.diagram] 절은 클래스 다이어그램을 소개하는데, 프로그램을 이루는 클래스들과 그들 간의 관계를 보여줍니다.

이 다이어그램들은 통합 모델링 언어 (UML, Unified Modeling Language)에 기반하는데, 프로그램, 특히 객체 지향형 프로그램, 설계를 전달하기 위해 소프트웨어 엔지니어들이 사용하는 표준화된 도안형(graphical) 언어입니다.

UML 은 객체와 클래스들간의 여러 가지 관계들을 표현하는 다양한 다이어그램들을 포함하는 풍성한 언어입니다. 제가 이 책에서 소개한 것들은 언어의 일부입니다만, 실제로 가장 자주 사용되는 것들입니다.

이 부록의 목적은 앞선 장들에 소개된 다이어그램들을 리뷰하고, Lumpy 를 소개하는 것입니다. Lumpy, “UML in Python” 을 나타내는데 글자들을 재배치했습니다,는 Swampy의 일부인데 [turtlechap] 장 이나 [tkinter] 장의 사례 연구를 공부했거나, 연습 [canvas]을 했다면 이미 설치했습니다.

Lumpy 는 파이썬의 inspect 모듈을 사용해서 실행중인 프로그램의 상태를 조사하고 (스택 다이어그램을 포함하는) 객체 다이어그램과 클래스 다이어그램을 만들어냅니다.

상태도

lumpydemo1

[fig.lumpy1]

여기 Lumpy 로 상태도를 만드는 예가 있습니다.

from swampy.Lumpy import Lumpy

lumpy = Lumpy()
lumpy.make_reference()

message = 'And now for something completely different'
n = 17
pi = 3.1415926535897932

lumpy.object_diagram()

첫 번째 줄은 swampy.Lumpy 에서 Lumpy 클래스를 들여옵니다. Swampy 를 패키지로 설치하지 않았다면, Swampy 파일들이 파이썬의 검색 경로에 있는지 확인한 후 대신 이 import 문을 사용하세요:

from Lumpy import Lumpy

다음 줄은 Lumpy 객체를 만들고 “기준점(reference point)” 을 만드는데, Lumpy 가 지금까지 정의된 객체들을 기록한다는 뜻입니다.

다음으로 새 변수들을 정의하고 object_diagram 를 호출하는데, 기준점 이후로 정의된 객체들, 이 경우엔 message, n, pi,을 그립니다.

그림 [fig.lumpy1]는 결과를 보여줍니다. 도안 스타일은 앞에서 제가 보여준 것들과 다릅니다; 예를 들어, 각 참조는 옆에 변수 명이 있는 원과 값으로 가는 선으로 표현됩니다. 그리고 긴 문자열은 잘립니다. 하지만 다이어그램이 전달하는 정보는 동일합니다.

변수 명은 <module>,모듈 수준의 변수, 즉 전역임을 나타냅니다, 로 표시한 프레임에 있습니다.

이 예를 http://thinkpython.com/code/lumpy_demo1.py에서 다운로드 할 수 있습니다. 대입 문 몇 개를 추가한 후 다이어그램이 어떻게 되는지 보세요.

스택 다이어그램

lumpydemo2

[fig.lumpy2]

여기에 Lumpy 로 스택 다이어그램을 만드는 예가 있습니다. http://thinkpython.com/code/lumpy_demo2.py에서 다운로드 할 수 있습니다.

from swampy.Lumpy import Lumpy

def countdown(n):
    if n <= 0:
        print 'Blastoff!'
        lumpy.object_diagram()
    else:
        print n
        countdown(n-1)

lumpy = Lumpy()
lumpy.make_reference()
countdown(3)

그림 [fig.lumpy2]는 결과를 보여줍니다. 각 프레임은 밖에 함수의 이름이, 안에 변수들이 있는 상자로 표시됩니다. 함수가 재귀적이기 때문에, 각 재귀 수준마다 하나의 프레임이 있습니다.

스택 다이어그램은 실행의 특정한 지점에서의 프로그램 상태를 보여준다는 점을 기억하세요. 여러분이 원하는 다이어그램을 얻으려면, 때로 어디에서 object_diagram 을 호출할지 숙고해야 합니다.

이 경우에 저는 재귀의 기저 사례를 실행한 직후에 object_diagram를 호출했습니다; 이런 방법으로 스택 다이어그램은 재귀의 각 수준을 보여줍니다. 프로그램 실행의 연속적인 스냅샷을 얻기 위해 object_diagram 를 여러 번 호출할 수 있습니다.

객체 다이어그램

lumpydemo3

[fig.lumpy3]

이 예는 [sequence] 절의 리스트들을 보여주는 객체 다이어그램을 만듭니다. http://thinkpython.com/code/lumpy_demo3.py 에서 다운로드 할 수 있습니다.

from swampy.Lumpy import Lumpy

lumpy = Lumpy()
lumpy.make_reference()

cheeses = ['Cheddar', 'Edam', 'Gouda']
numbers = [17, 123]
empty = []

lumpy.object_diagram()

그림 [fig.lumpy3]은 결과를 보여줍니다. 리스트들은 요소들에 대응하는 지수들을 보여주는 상자로 표시됩니다. 이 표현은 약간 오도하는 부분이 있는데, 지수는 사실 리스트의 일부가 아니기 때문입니다. 하지만 이 것이 다이어그램을 좀 더 읽기 쉽도록 만든다고 생각합니다. 빈 리스트는 빈 상자로 표시됩니다.

lumpydemo4

[fig.lumpy4]

그리고 여기에는 [invert] 절의 딕셔너리를 보여주는 예가 있습니다. http://thinkpython.com/code/lumpy_demo4.py에서 다운로드 할 수 있습니다.

from swampy.Lumpy import Lumpy

lumpy = Lumpy()
lumpy.make_reference()

hist = histogram('parrot')
inverse = invert_dict(hist)

lumpy.object_diagram()

그림 [fig.lumpy4]는 결과를 보여줍니다. hist 는 문자 (한 글자 문자열) 를 정수로 대응시키는 딕셔너리입니다; inverse 는 정수를 문자열의 리스트로 대응시킵니다.

lumpydemo5

[fig.lumpy5]

이 예는 [copying] 절에서와 같은 Point 와 Rectangle 객체의 객체 다이어그램을 만듭니다. http://thinkpython.com/code/lumpy_demo5.py에서 다운로드 할 수 있습니다.

import copy
from swampy.Lumpy import Lumpy

lumpy = Lumpy()
lumpy.make_reference()

box = Rectangle()
box.width = 100.0
box.height = 200.0
box.corner = Point()
box.corner.x = 0.0
box.corner.y = 0.0

box2 = copy.copy(box)

lumpy.object_diagram()

그림 [fig.lumpy5]는 결과를 보여줍니다. copy.copy 는 얕은 복사를 하기 때문에, box 와 box2 는 자신만의 width 와 height를 갖지만, 내장된 Point 객체는 공유합니다. 이런 종류의 공유는 보통 수정 불가능한 객체들의 경우에는 문제되지 않습니다만, 수정 가능한 형들의 경우는 오류를 일으킬 가능성이 높습니다.

함수와 클래스 객체

lumpydemo6

[fig.lumpy6]

Lumpy 로 객체 다이어그램을 만들 때, 저는 보통 기준점을 만들기 전에 함수와 클래스를 정의합니다. 이런 방법으로, 함수와 클래스 객체는 다이어그램에 나타나지 않습니다.

하지만 함수와 클래스를 매개변수로 전달한다면, 이들도 나타나길 원할 겁니다. 이 예는 어떻게 되는지 보여줍니다; http://thinkpython.com/code/lumpy_demo6.py 에서 다운로드 할 수 있습니다.

import copy
from swampy.Lumpy import Lumpy

lumpy = Lumpy()
lumpy.make_reference()

class Point(object):
    """Represents a point in 2-D space."""

class Rectangle(object):
    """Represents a rectangle."""

def instantiate(constructor):
    """Instantiates a new object."""
    obj = constructor()
    lumpy.object_diagram()
    return obj

point = instantiate(Point)

그림 [fig.lumpy6]은 결과를 보여줍니다. object_diagram 를 함수 안에서 호출하기 때문에, 모듈 수준 변수들의 프레임과 instantiate 호출의 프레임에 대한 스택 다이어그램을 얻습니다.

모듈 수준에서, Point 와 Rectangle 은 클래스 객체 (type 형을 갖습니다)를 가리킵니다; instantiate 는 함수 객체를 가리킵니다.

이 다이어그램은 흔히 혼동하는 두 가지를 분명히 합니다: (1) 클래스 객체, Point,와 Point 의 인스턴스, obj,간의 차이와 (2) instantiate 가 정의될 때 만들어지는 함수 객체와 그 것이 호출될 때 만들어지는 프레임간의 차이.

클래스 다이어그램

lumpydemo7

[fig.lumpy7]

lumpydemo8

[fig.lumpy8]

제가 상태도, 스택 다이어그램, 객체 다이어그램들을 구분하기는 하지만, 그들은 거의 같은 것들입니다: 특정 시간에서 실행중인 프로그램의 상태를 보여줍니다.

클래스 다이어그램은 다릅니다. 프로그램을 이루는 클래스들과 그들 간의 관계를 보여줍니다. 특정 시점이 아니라 프로그램을 전체로서 기술한다는 의미에서 시간과 무관합니다. 예를 들어, 클래스 A 의 인스턴스가 일반적으로 클래스 B 의 인스턴스에 대한 참조를 갖고 있다면, 이 클래스들 간에는 “HAS-A 관계”가 있다고 말합니다.

여기 HAS-A 관계를 보여주는 예가 있습니다. http://thinkpython.com/code/lumpy_demo7.py에서 다운로드 할 수 있습니다.

from swampy.Lumpy import Lumpy

lumpy = Lumpy()
lumpy.make_reference()

box = Rectangle()
box.width = 100.0
box.height = 200.0
box.corner = Point()
box.corner.x = 0.0
box.corner.y = 0.0

lumpy.class_diagram()

그림 [fig.lumpy7]은 결과를 보여줍니다. 각 클래스는 클래스, 클래스가 제공하는 메쏘드들, 클래스 변수들, 인스턴스 변수들의 이름을 담은 상자로 표현됩니다. 이 예에서, Rectangle 과 Point 는 인스턴스 변수들이 있습니다만, 메쏘드와 클래스 변수는 없습니다.

Rectangle 에서 Point 로 가는 화살표는 Rectangle 에 내장된 Point 가 있음을 보여줍니다. 여기에 더해, Rectangle 과 Point는 모두 object 를 계승하는데, 다이어그램에서 삼각형 머리 화살표로 표현됩니다.

여기 연습 [poker] 에 대한 제 해답에 사용된 좀 더 복잡한 예가 있습니다. http://thinkpython.com/code/lumpy_demo8.py 에서 코드를 다운로드 할 수 있습니다; http://thinkpython.com/code/PokerHand.py 또한 필요할겁니다.

from swampy.Lumpy import Lumpy

from PokerHand import *

lumpy = Lumpy()
lumpy.make_reference()

deck = Deck()
hand = PokerHand()
deck.move_cards(hand, 7)

lumpy.class_diagram()

그림 [fig.lumpy8]은 결과를 보여줍니다. PokerHand 는 Hand를 계승하고, Hand 는 다시 Deck를 계승합니다. Deck 과 PokerHand 모두 Card 를 포함합니다.

이 다이어그램은 Hand 또한 Card 를 포함한다는 것을 보여주지 못하는데, 프로그램에 Hand 의 인스턴스가 없기 때문입니다. 이 예는 Lumpy 의 한계를 보여줍니다; 단지 인스턴스화된 객체들의 애트리뷰트와 HAS-A 관계에 대한 것만 압니다.