오리 타임검사(Duck Typeing)와 파이썬

By | 2013/03/03

이번 포스팅에서 다룰 이야기는조엘이 엄선한 소프트웨어 블로그 29선 이라는 책에서 에서 "타입검사와 테스트"라는 제목으로 언급된 내용이다. 블로그 원문의 제목은 "Strong Typing vs. Strong Testing" 이고 Bruce Eckel에 의해 작성되었다. (책에 실린 원문 URL은 이미 깨져서 접속할 수 없지만 검색을 통해 원문 내용은 찾아볼 수 있다.)

프로그래밍 언어는 정적 타입검사와 동적 타입검사 둘 중 한 가지의 방식을 채택하고 있다. 정적 타입검사는 컴파일러가 컴파일 시점에 변수들의 '타입'을 검사하는 것이고, 동적 타입검사는 컴파일 시점에는 '타입'을 검사하지 않고, 실행 시에 '타입'이 맞지 않으면 에러를 발생시키는 방법이다.


// Speaking pets in Java:
interface Pet {
void speak();
}

class Cat implements Pet {
public void speak() { System.out.println("meow!"); }
}

class Dog implements Pet {
public void speak() { System.out.println("woof!"); }
}

public class PetSpeak {
static void command(Pet p) { p.speak(); }
public static void main(String[] args) {
Pet[] pets = { new Cat(), new Dog() };
for(int i = 0; i < pets.length; i++) command(pets[i]); } } [/java] 위의 자바 코드는 정적 타임검사때문에 Cat, Dog를 Pet으로 업캐스팅하고 있다. command()는 오직 Pet '타입'을 인자로 받고 있기 때문에 command()의 인자로 Pet을 구현(implements)한 Cat, Dog를 Pet으로 업캐스팅 해야만 "meow" 또는 "woof" 를 출력할 수 있다. 만약 command()의 인자가 Pet이 아닌 Cat을 인자로 받는 함수였다면? Pet, Dog 타입은 command()의 인자가 될 수 없어서 에러를 내뱉고 말 것이다. 하지만 Dog든 Cat이든 결국 똑같은 Speak()를 갖고 있는데... [python] # Speaking pets in Python: class Pet: def speak(self): pass class Cat(Pet): def speak(self): print "meow!" class Dog(Pet): def speak(self): print "woof!" def command(pet): pet.speak() pets = [ Cat(), Dog() ] for pet in pets: command(pet) [/python] 파이썬은 자바와 다르게 동적 타입검사를 한다. 책에서는 이 개념을 두고 오리 타입검사(Duck Typing)라고 불리기도 한다는데, 오리처럼 걷고 오리처럼 소리 내는 것은 오리로 간주하면 된다는 뜻이라고 한다. command()는 pet을 인자로 받고 있는데, pet으로 Pet, Cat, Dog 타입 뿐만 아니라 그 어떤 타입이 오더라도 speak()만 갖고 있는 타입이라면 에러가 발생하지 않는다. 뭐 어쨌든 인자로 들어온 객체가 speak()를 구현하고 있으면 그 구현 내용을 실행해주면 된다는 것이다.

동적 타입검사는 실행시 어떠한 문제가 터질지 모르기 때문에 위험하다고 볼 수 있지만, 정적 타입검사가 프로그램의 안정성을 보장해준다고 할 수는 없다. 타입검사는 테스트의 일부에 불과하기 때문이다. 정적 타입검사를 하는 언어를 사용한 프로그램을 컴파일하는데 성공한 것은 프로그램의 정확성에 대한 테스트를 일부 마친 것에 불과하다는 말이다. 코드의 정확성을 보장하는 방법은 어떤 타입검사를 하건 간에 유닛 테스트 등의 여러가지 테스트를 모두 통과하는지 확인하는 것이다. 그래서 Bruce Eckel는 "타입검사보다는 테스트에 더 공을 들이자" 라고 말하고 있다.