상세 컨텐츠

본문 제목

Duck typing 과 Nominative typing, Structure typing

개발/python-객체지향프로그래밍(OOP)

by Matthew0633 2022. 5. 25. 23:49

본문

타이핑 방식은 ‘타입이 같은가’, ‘타입이 호환가능한가’ 를 체크하기 위한 방식이다. 이러한 타이핑 방식에는 Nominative typing, Structure typing 방식이 있다. 그리고 내가 주로 사용하는 파이썬에서 매우 자주 사용되는 Duck typing 까지 살펴보고자 한다.

 

Nominative typing (명시적 타이핑)

In computer science, a type system is a nominal or nominative type system (or name-based type system) if compatibility and equivalence of data types is determined by explicit declarations and/or the name of the types. Nominal systems are used to determine if types are equivalent, as well as if a type is a subtype of another.(https://en.wikipedia.org/wiki/Structural_type_system))

인용문에 나와있듯, 명시적 타이핑 방식에서는 선언할 때의 타입이 같은가에 따라 타입 체크가 이루어진다. 단순하지만, 아주 타입에 엄격한 방식이라고 할 수 있다. type-safety 한 프로그래밍이 가능한 장점이 있고 또한, 유연성 감소로 인한 생산성이 비교적 떨어진다는 단점이 있다. C++, C#, Java, Swift 등의 언어에서 주로 사용되는 방식이다. 특히 sub-typing 에서는 유명한 OOP 언어들의 경우 명시적 타이핑을 사용하고 있다.

아래는 명시적 타이핑을 이해하기 위한 pseudo 코드이다. 명시적 타이핑 방식에서 변수 foo 는 Foo type 의 객체를 가질 수 있는데, Bar 의 객체를 할당했으므로 에러가 발생한다.

class Foo { method(input: string) { /* ... */ } }
class Bar { method(input: string) { /* ... */ } }

let foo: Foo = new Bar(); // Error!

 

Structure typing (구조적 타이핑)

In structural typing, an element is considered to be compatible with another if, for each feature within the second element's type, a corresponding and identical feature exists in the first element's type. ... Two types are considered to be identical if each is compatible with the other.
(https://en.wikipedia.org/wiki/Structural_type_system)

선언과 명시를 통해 type 을 결정하고, 이를 기준으로 같은지 판단하는 명시적 타이핑 방식과 달리, 구조적 타이핑 방식에서는 구조가 같다면 같은 type으로 판단하며 호환가능하다. 구조가 같다는 것은, 속성과 기능 구조가 동일하고, 내부의 type 또한 동일하다는 것으로 이해할 수 있다.

아래 예제의 첫번째 case 를 보면, Foo 와 Bar 의 객체는 호환 가능하다. 동일한 구조를 가지고 있기 때문이다. 그러나, 두번째 case의 경우 메소드의 인자 type 이 달라 기능이 Bar type의 구조가 상이하여 foo 에 Bar class 의 객체가 할당될 수 없다.

// case1
class Foo { method(input: string) { /* ... */ } }
class Bar { method(input: string) { /* ... */ } }
let foo: Foo = new Bar(); // Works!

// case2
class Foo { method(input: string) { /* ... */ } }
class Bar { method(input: number) { /* ... */ } }
let foo: Foo = new Bar(); // Error!

속성과 구조에 의해 type 에 대한 호환성이 결정되기에, 명시적 타이핑 방식보다 생산성이 좋은 방식이다.

Duck typing (덕타이핑)

Duck typing in computer programming is an application of the duck test —"If it walks like a duck and it quacks like a duck, then it must be a duck"—to determine whether an object can be used for a particular purpose. ... In duck typing, an object is of a given type if it has all methods and properties required by that type.
https://en.wikipedia.org/wiki/Duck_typing

덕타이핑의 개념은 구조적 타이핑과 매우 유사하다. 명시적 선언이 type을 결정하는 것이 아니라, ‘어떤 구조와 기능을 가지고 그것들이 어떻게 사용되는가’ 가 결정하는 방식이다. 방식의 이름과 관련지어보자면, 오리처럼 울고, 오리처럼 걷는다면 그것이 원래 오리가 아니라고 하더라도 오리 type 과 호환되게끔 코드를 작성하는 방식이 덕타이핑이다.

구조적 타이핑과의 차이점은, 구조적 타이핑 은 컴파일 타이밍에 type check 이 일어나는 반면, 덕타이핑은 type check 이 런타임 타이밍에 일어나거나 혹은 하지 않는다는 점이다. 그리고 구조적 타이핑은 정적 타이핑에서, 덕타이핑은 동적 타이핑에서 주로 사용된다.

파이썬의 tutorial 에서도 duck typing 에 대한 설명이 나와 있다.

Pythonic programming style that determines an object's type by inspection of its method or attribute signature rather than by explicit relationship to some type object ("If it looks like a duck and quacks like a duck, it must be a duck.") By emphasizing interfaces rather than specific types, well-designed code improves its flexibility by allowing polymorphic substitution. Duck-typing avoids tests using type() or isinstance(). Instead, it typically employs hasattr() tests or EAFP (Easier to Ask Forgiveness than Permission) programming.

파이썬에서도 duck-typing 방식의 사용은 다형성을 제공하여, 높은 유연성을 제공할 수 있다고 말한다. 자유로운 프로그래밍과 유연함을 강조하는 파이썬의 철학에 어울리는 방식이라 할 수 있다.

duck-typing 방식으로 코드를 작성할 때는, 의 사용을 피하고 오리의 속성을 갖고 있으면 오리로 판단하도록 type()isinstance() 보다는 hasattr() 을 test 코드에 사용하도록 권장하고 있다.

아래는 duck-typing 에 대한 Wiki 의 예시이다. Duck 와 Whale 는 둘 다 헤엄칠 수 있지만, Duck 만 날 수 있다. 따라서 for 문에서 Whale 는 Duck 와 완벽히 호환되는 type 으로 작동하지 않는다. 그러나 for문에서 호출하는대로, 어떤 class 든 swim() 과 fly() 메소드만 있다면, Duck 과 호환 가능하다.

# Duck Typing - python3
class Duck:
    def swim(self):
        print("Duck swimming")

    def fly(self):
        print("Duck flying")

class Whale:
    def swim(self):
        print("Whale swimming")

for animal in [Duck(), Whale()]:
    animal.swim()
    animal.fly()  # Whale 은 fly() 가 없으므로, Duck 과 호환되지 않는다.

<Reference>

https://en.wikipedia.org/wiki/Nominal_type_system

https://flow.org/en/docs/lang/nominal-structural/

https://en.wikipedia.org/wiki/Structural_type_system

https://vallista.kr/덕-타이핑과-구조적-타이핑/

https://en.wikipedia.org/wiki/Duck_typing

'개발 > python-객체지향프로그래밍(OOP)' 카테고리의 다른 글

Context Manager  (0) 2022.05.27
Mix-in Class  (0) 2022.05.25
Type Annotation (9) - Generic Type  (0) 2022.05.25
Type Annotation (8) - Final Type  (0) 2022.05.23
Type Annotation (7) - Type alias  (0) 2022.05.23

관련글 더보기

댓글 영역