Iteration – JavaScript, Python

자바스크립트와 파이썬에서 사용되는 iteration을 비교했다. 연속된 데이터를 다루는 방법으로 자바스크립트는 Array라는 객체를 사용하고 파이썬은 list 라는 타입을 사용한다.

// JavaScript
arr = new Array(1,2)  // [1, 2]
# Python
arr = list([1, 2])  # [1, 2]

두 언어에서 리터럴 문법으로 표현하면 아래와 같은 코드로 표현할 수 있다.

arr = [1, 2]

for 문을 사용하여 Array와 list를 순서대로 읽는 방법도 비슷하다.

// JavaScript
> arr = [1, 2]
[ 1, 2 ]
> for (let a of arr) {
...   console.log(a);
... }
1
2
# Python
>>> arr = [1, 2]
>>> for a in arr:
...     print(a)
...
1
2

JavaScript Iteration

문법이 조금 다르지만 익숙한 코드다. 만약에 자바스크립에서 for of 문에 배열이 아닌 객체를 전달하면 어떻게 될까?

> for (let a of {}) {
...   console.log(a);
... }
TypeError: (intermediate value)[Symbol.iterator] is not a function

[Symbol.iterator] 함수가 없다고 에러가 난다. 그렇다면 배열인 arr 객체는 [Symbol.iterator] 함수를 가지고 있는지 먼저 알아보자

> arr = [1, 2]
[ 1, 2 ]
> whatFunction = arr[Symbol.iterator]
[Function: values]

가지고 있다. values라는 이름도 가지고 있다고 알려준다. 이 함수를 수행하면 iterator 객체를 돌려준다. 말하자면 for of 문에 삽입될 객체는 Array 객체여야 한다는 것이고 이 객체는 [Symbol.iterator] 함수를 속성으로 가지고 있다는 뜻이다. 그렇다면 [Symbol.iterator]의 동작을 테스트해 보자.

> arr = [1, 2]
[ 1, 2 ]
> iteratorObject = arr[Symbol.iterator]()
{}

이렇게 얻은 iteratorObject 객체는 next 함수를 가지고 있다. 이 함수를 호출하면 처음 배열 값 하나를 돌려준다. 이 함수를 다시 한번 호출하면 두번째 배열 값을 돌려준다.

> iteratorObject.next()
{ value: 1, done: false }
> iteratorObject.next()
{ value: 2, done: false }

이런 방식으로 지정된 순서대로 값을 읽는 방식을 iteration이라고 하고 이런 규칙을 iterator 프로토콜이라고한다. 뻔히 배열이 있는데 이런 복잡한 방법을 사용하는 이유는 객체에 [Symbol.iterator] 함수만 구현하면 이 객체는 iterable 객체가 되고 for of 문에서 자유롭게 사용할 수 있기 때문이다. 얼마나 자유로운지 빈 객체에 [Symbol.iterator] 함수를 추가해 보자

iterableObject = {}
iterableObject[Symbol.iterator] = function () {
  return {
    arr: [1, 2],
    next() {
      const head = this.arr.shift();
      return {
        value: head,
        done: (head ? false : true)
      };
    }
  }
}

[Symbol.iterator] 함수는 객체를 돌려주는데 이 객체는 두 항목을 가지는 배열과 이 항목들의 값을 돌려주는 next 함수를 가지고 있다. 이 함수는 호출 되면 내부 배열의 값을 순서대로 돌려준다. 값과 함께 배열의 마지막을 알려주는 done 속성도 돌려준다. 이 값이 true이면 더 이상 돌려줄 값이 없다는 뜻이다. 객체에 특별한 함수 하나만 구현하면 바로 for of 문에서 사용할 수 있다.

> for (let a of iterableObject) {
...   console.log(a);
... }
1
2

객체의 타입이 중요한 것이 아니고 필요한 함수를 가지고 있는지가 중요한 것이다. Duck typing의 한 예라고 볼수 있다.

Python Iteration

파이썬도 이런 방식을 사용한다. 파이썬의 iteration을 알아보자. 자바스크립트에서 사용한 방법으로 에러를 만들기 위해 for in 문을 사용할 때 리스트 대신 객체를 사용해보자.

>>> for a in object():
...     print(a)
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'object' object is not iterable

에러가 발생한다. 객체가 iterable 하지 않다는 것이다. 파이썬에서 객체가 iterable 하다는 것은 __iter__라는 함수를 가지고 있다는 것을 의미한다. 그렇다면 for in 문에서 잘 동작하는 list 객체가 __iter__ 객체를 가지고 있는지 확인하자

>>> arr = [1, 2]
>>> arr.__iter__
<method-wrapper '__iter__' of list object at 0x7fc1b19cbb48>

__iter__ 객체를 가지고 있는 것을 확인할 수 있다. arr.__iter__는 자바스크립트의 [Symbol.iterator] 함수와 비슷한 역활을 함으로 이 함수를 수행하여 iterator를 얻을 수 있다.

arr = [1, 2]
iterator = arr.__iter__()

이렇게 얻은 파이썬의 iterator는 __next__ 함수를 가지고 있다. 자바스크립트의 next와 비슷한 기능을 한다. 아래 명령으로 처음 값과 두 번째 값을 가져올 수 있다.

>>> iterator.__next__()
1
>>> iterator.__next__()
2

자바스크립트 iterator의 next 함수는 값과 함께 done 이라는 속성의 플래그를 전달하여 데이터의 끝을 알려주지만 파이썬 iterator의 __next__ 함수는 값 만을 돌려주며 StopIteration 이라는 예외를 발생하여 끝을 알려주는 것이 다르다. 파이썬에서도 자바스크립트 처럼 iterable 객체를 만들 수 있다. 단, 리터럴 문법으로 객체를 만들 수 없음으로 클래스를 사용해야 한다. 자바스크립트가 이런 면에선 간편하긴 하다. 먼저 __iter____next__ 를 제공하는 클래스를 만든다.

class IterableObject:
    def __init__(self):
        self.arr = [1, 2]

    def __iter__(self):
        return self

    def __next__(self):
        if len(self.arr) > 0:
            value = self.arr[0]
            self.arr = self.arr[1:]
            return value
        else:
            raise StopIteration()

이 클래스는 내부에 2개의 항목을 가지는 리스트를 가지고 있으며 __next__ 함수가 호출되면 순서대로 값을 돌려준다. 돌려줄 데이터가 없으면 StopIteration 예외를 발생시킨다. 이제 이 클래스를 사용하여 iterable 객체를 만들고 for in 을 사용하여 값을 확인할 수 있다.

>>> iterableObject = IterableObject()
>>> for a in iterableObject:
...     print(a)
...
1
2

파이썬에서는 __로 시작하는 함수의 직접 사용을 권장하지 않음으로 여러 내장 함수를 사용하여 iterable 객체의 값을 읽을 수 있다. 일부러 한 줄로 표현해 봤다.

>>> print(next(iter(IterableObject())))
1

마무리

자바스크립트와 파이썬은 iteration에 관해서는 개념과 사용방법이 비슷하다. 누가 누구를 닮은 건지 모르겠다. 데이터를 반복해서 읽는 작업을 할 경우에 배열이나 리스트 말고도 iterable 객체를 사용할 수 있다. for 문을 사용하여 접근할 수 있는 것은 너무나도 직관적이지만 이 목적으로만 사용되는 것은 아니다. 두 언어 모두 객체에 함수를 추가하는 것만으로 iterable 객체를 만들수 있다는 것이 장점이다. 프로그래밍 언어 자체가 지원해야 가능한 방식이다. 다음 글에선 연속된 데이터에 접근하는 좀 더 강력한 방식인 generator를 비교할 예정이다.

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Google photo

Google의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중

This site uses Akismet to reduce spam. Learn how your comment data is processed.