Python list vs. JavaScript Array

파이썬을 좀 더 잘 이해하기 위해 자바스크립트와 비교하고 있다.

[]

이 문법은 자바스크립트에서는 배열을 나타내고 파이썬에서는 리스트 데이터 타입을 나타낸다. map과 같은 함수를 사용할 때 이 두 데이터 타입은 두 언어에서 비슷하게 느껴진다. 사용하는 메서드와 사용되는 방식 그리고 파이썬의 지연된 list 계산 방법을 비교하겠다. 아래 버전을 사용한다.

$ python -V
Python 3.6.0
$ node --version
v8.0.0

1. [] 의 Type

파이썬에서 type 함수는 전달된 인자의 타입을 나타낸다. [] 전달하면 list 클래스라고 알려준다. list 함수를 수행한 결과를 전달해도 같다. []와 list()는 같다. 파이썬은 type이나 list 같은 내장 함수를 여럿 가지고 있다.

    >>> # python
    >>> type([])
    <class 'list'>
    >>> type(list())
    <class 'list'>
    >>> [] == list()
    True

자바스크립트에서 []를 typeof 키워드로 확인하면 []가 객체 타입임을 알 수 있다. 배열인지 확인하려면 Array.isArray를 사용해야 한다. []는 Array이면서 객체이다.

    > // javascript
    > typeof([])
    'object'
    > Array.isArray([])
    true

[]는 new Array()를 사용하는 것과 거의 같다. Array는 전역 객체이며 Array()는 Array의 생성자 함수다. []는 Array의 프로토타입에 정의된 여러 메서드를 사용할 수 있다. Array는 new 키워드를 사용하지만 클래스는 아니다.

    > // javascript
    > typeof(new Array())
    'object'
    > Array.isArray(new Array())
    true

instanceof 키워드를 사용하면 [] 과 Array가 같은 생성자를 사용하여 만들어진 객체임을 알 수 있다.

    > // javascript
    > [] instanceof Array
    true
    > new Array() instanceof Array
    true

2. Python list 메서드

파이썬 투토리얼에서 설명하는 list 데이터 타입은 11개의 메서드를 가지고 있는데 자세한 분석을 위해 대괄호에 숫자를 입력해서 값을 가져오는 인덱싱, 콜론을 사용하여 인덱싱 범위를 읽는 슬라이싱을 사용하겠다. “>>>”는 파이썬 쉘을 의미하는 프롬프트이고 “>”는 자바스크립트 쉘을 의미하는 프롬프트이다. 파이썬 쉘이나 자바스크립트 쉘에서 변수 혹은 평가식을 입력하면 그 값을 보여준다. 리스트 데이터 타입을 리스트라고 부르겠다.

파이썬에서 대괄호를 사용하여 리스트를 만들 수 있고 대괄호 안에 콤마로 분리되는 값을 넣으면 초기 값으로 지정할 수 있다. 아래 코드에서 1이라는 단 하나의 값이 초기 값으로 지정되었다.

    >>> # python
    >>> a = [1]
    >>> a
    [1]

자바스크립트 배열을 사용하여 작성하면 아래와 같다. 파이썬 코드와 동일하다. 자바스크립트에서는 일반적으로 var, let 혹은 const 키워드를 사용하여 변수를 선언하지만 생략할 수 있다.

    > // javascript
    > a = [1]
    [ 1 ]
    > a
    [ 1 ]

파이썬과 자바스크립트 쉘 동작이 조금 다른 것을 볼 수 있는데 파이썬 코드에서 a = [1] 수행 후 쉘은 아무 출력을 하지 않는다. 그러나 자바스크립트 코드 a = [1] 수행 후에는 [ 1 ] 값이 표시된다. 이는 쉘의 특성이 아니고 할당 문을 호출한 이후 파이썬은 리턴 값이 없지만 자바스크립트는 평가된 값을 리턴하는 언어의 특성이다. 자바스크립트의 모든 변수와 평가식 그리고 함수는 return 값을 가지는데 그 값이 없을 경우에도 undefined을 리턴한다.

3. append

파이썬에서 리스트에 항목 추가하기 위해 append 메서드를 사용한다.

    >>> # python
    >>> a.append(2)
    >>> a
    [1, 2]

자바스크립트에서는 push 메서드를 사용한다. 메서드 이름이 다를 뿐 결과는 동일하다.

    > // javascript
    > a.push(2)
    2
    > a
    [ 1, 2 ]

파이썬에서 메서드를 사용하지 않고 슬라이싱을 사용하여 리스트에 마지막에 새 항목을 추가할 수도 있다. 파이썬에서 리스트 길이는 len이라는 내장 함수를 사용하여 알 수 있음으로 append 메서드 대신 슬라이싱을 사용하여 리스트 마지막에 9 값을 추가하는 코드는 아래와 같다.

    >>> # python
    >>> a[len(a):] = [9]
    >>> a
    [1, 2, 9]

자바스크립트에서도 동일한 방법을 사용할 수 있는데 배열의 길이를 알기 위해 배열의 length 속성을 사용한다.

    > // javascript
    > a[a.length] = 9
    9
    > a
    [ 1, 2, 9 ]

자바스크립트에서는 배열의 인덱스를 벗어나는 접근을 해도 에러가 발생하지 않는다. 크기를 벗어난 인덱스로 접근이 요청되면 배열의 크기를 자동으로 늘어난다. 초기화되지 않은 항목은 undefined으로 초기화된다. 이와 다르게 파이썬의 리스트에서는 인덱스가 본래 크기의 범위를 벗어나면 IndexError가 발생한다.

4. extend

파이썬에서 리스트 끝에 여러 항목을 추가하기 위해 extend 메서드를 사용한다. 슬라이싱을 사용할 수도 있다. 아래 코드에서 range는 주어진 가변 항목들을 list로 만들어주는 내장 함수다. range(4, 6)은 [ 4, 5 ] 의미이다.

    >>> # python
    >>> a.extend(range(4, 6))
    >>> a
    [1, 2, 9, 4, 5]
    >>> a[len(a):] = range(5, 8)
    >>> a
    [1, 2, 9, 4, 5, 5, 6, 7]

자바스크립트에서는 concat 메서드를 사용하여 여러 항목들을 배열 마지막에 추가할 수 있다. 항목 삽입, 삭제, 읽기 메서드인 splice를 사용할 수도 있다. a.splice(a.length, 0, 5, 6, 7)는 a 배열에 마지막 위치에 항목 삭제는 하지 말고 5, 6, 7 값을 추가한다는 의미다.

    > // javascript
    > a = a.concat([4, 5])
    [ 1, 2, 9, 4, 5 ]
    > a
    [ 1, 2, 9, 4, 5 ]
    > a.splice(a.length, 0, 5, 6, 7)
    []
    > a
    [ 1, 2, 9, 4, 5, 5, 6, 7 ]

5. insert

파이썬 리스트에서 항목 삽입은 insert 메서드를 사용한다. 삽입할 위치의 인덱스 값과 삽입할 값을 전달한다.

    >>> # python
    >>> a.insert(1, 0)
    >>> a
    [1, 0, 2, 9, 4, 5, 5, 6, 7]
    >>> a.insert(len(a) - 1, 8)
    >>> a
    [1, 0, 2, 9, 4, 5, 5, 6, 8, 7]

자바스크립트에서 배열 항목 삽입 전용 메서드는 없으나 삽입, 삭제, 읽기 메서드인 splice를 사용할 수 있다.

    > // javascript
    > a.splice(1, 0, 0)
    []
    > a
    [ 1, 0, 2, 9, 4, 5, 5, 6, 7 ]
    > a.splice(a.length - 1, 0, 8)
    []
    > a
    [ 1, 0, 2, 9, 4, 5, 5, 6, 8, 7 ]

6. remove

파이썬 리스트에서 remove 메서드에 값을 전달하면 처음 발견된 항목을 삭제한다.

    >>> # python
    >>> a.remove(9)
    >>> a
    [1, 0, 2, 4, 5, 5, 6, 8, 7]

자바스크립트에서 배열 항목 삭제 전용 메서드는 없으나 삽입, 삭제, 읽기 메서드인 splice를 사용할 수 있다.

    > // javascript
    > a.splice(a.indexOf(9), 1)
    [ 9 ]
    > a
    [ 1, 0, 2, 4, 5, 5, 6, 8, 7 ]

7. pop

파이썬 리스트에서 가장 마지막에 있는 항목은 pop 메서드를 사용하여 삭제와 동시에 읽을 수 있다.

    >>> # python
    >>> a.pop()
    7
    >>> a
    [1, 0, 2, 4, 5, 5, 6, 8]

자바스크립트 배열에도 같은 기능의 pop 메서드가 있다.

    > // javascript
    > a.pop()
    7
    > a
    [ 1, 0, 2, 4, 5, 5, 6, 8 ]

파이썬의 pop 메서드는 인자로 인덱스를 받는다. 0을 전달한다면 리스트의 처음 항목 삭제하면서 삭제한 항목을 돌려준다. 이 경우 자바스크립트의 shift 메서드와 같은 기능이다.

8. count

파이썬 리스트에서는 주어진 값의 전체 개수를 돌려주는 count 메서드가 있다. 아래 코드는 5의 개수를 돌려준다. print는 인자로 주어진 값을 출력하는 파이썬 내장 함수다.

    >>> # python
    >>> print(a.count(5))
    2

자바스크립트 배열은 파이썬 리스트의 count 같은 메서드가 없다. 그렇지만 Array의 reduce 함수를 사용하여 같은 동작을 수행할 수 있다. 함수로 만들거나 프로토타입에 추가할 수도 있겠지만 기능만 확인했다.

    > // javascript
    > ((ary, value) => { return ary.reduce((count, item) => {return item == value ? count + 1 : count}, 0) })(a, 5);
    2

9. index

파이썬 리스트에서 특정 값이 위치하는 인덱스 값을 읽을 수 있다. 아래 코드는 5 값이 처음 나타나는 위치는 인덱스 4라고 알려준다.

    >>> # python
    >>> a.index(5)
    4
    >>> a
    [1, 0, 2, 4, 5, 5, 6, 8]

자바스크립트 배열도 같은 기능의 indexOf 메서드가 있다.

    > // javascript
    > a.indexOf(5)
    4
    > a
    [ 1, 0, 2, 4, 5, 5, 6, 8 ]

10. reverse, sort

파이썬 리스트는 정렬 혹은 역순을 위한 sort와 reverse 메서드를 제공한다. 비슷한 메서드를 사용한다면

    >>> # python
    >>> a.reverse()
    >>> a
    [8, 6, 5, 5, 4, 2, 0, 1]
    >>> a.sort()
    >>> a
    [0, 1, 2, 4, 5, 5, 6, 8]

자바스크립트 배열도 같은 메서드를 제공한다.

    > // javascript
    > a.reverse()
    [ 8, 6, 5, 5, 4, 2, 0, 1 ]
    > a
    [ 8, 6, 5, 5, 4, 2, 0, 1 ]
    > a.sort()
    [ 0, 1, 2, 4, 5, 5, 6, 8 ]
    > a
    [ 0, 1, 2, 4, 5, 5, 6, 8 ]

두 언어의 sort 메서드는 비교 함수를 인자로 받을 수 있다.

11. copy, clear

파이썬 리스트는 전체 복사를 위한 copy 메서드와 전체 삭제를 위한 clear 메서드를 지원한다. clear 기능은 키워드 del과 리스트의 슬라이싱을 사용하여 대체 가능하다.

    >>> # python
    >>> b = a.copy()
    >>> b
    [0, 1, 2, 4, 5, 5, 6, 8]
    >>> a.clear()
    >>> a
    []
    >>> a = b
    >>> a
    [0, 1, 2, 4, 5, 5, 6, 8]
    >>> del a[:]
    >>> a
    []

자바스크립트 배열은 복사를 위한 메서드가 없다. 그렇지만 배열의 삽입, 삭제, 읽기가 가능한 splice 메서드를 사용하여 같은 동작을 할 수 있다. clear 기능도 없지만 배열의 length 속성에 0 값을 넣는 방식으로 구현할 수 있다. 기존 배열에 [] 빈 배열을 할당하는 방법이 일반적이다.

    > // javascript
    > b = a.slice()
    [ 0, 1, 2, 4, 5, 5, 6, 8 ]
    > b
    [ 0, 1, 2, 4, 5, 5, 6, 8 ]
    > a.length = 0;
    0
    > a
    []
    > a = b
    [ 0, 1, 2, 4, 5, 5, 6, 8 ]
    > a
    [ 0, 1, 2, 4, 5, 5, 6, 8 ]
    > a.splice(0, a.length)
    [ 0, 1, 2, 4, 5, 5, 6, 8 ]
    > a
    []

자바스크립트의 배열과 파이썬의 리스트는 비슷한 기능을 가지고 있다. 더욱 중요한 차이점은 데이터 타입 자체에 있지 않고 배열 혹은 리스트를 다루는 방법에 있다. map 메서드나 map 함수를 사용할 때 그 차이가 드러난다.

12. map 함수 동작 방식이 다르다.

자바스크립트의 Array는 map 메서드를 가지고 있다. 수행할 함수를 인자로 전달하면 각각의 항목에 전달한 함수를 수행하여 그 결과를 배열로 돌려준다.

    > // javascript
    > [1, 3, 5, 7].map(x => x**2)
    [ 1, 9, 25, 49 ]

자바스크립트의 map이 파이썬의 map 보다 좀 더 직관적이다. 계산하고 바로 결과 돌려준다. 아래는 위의 자바스크립트와 같은 기능을 하는 파이썬 코드다. 자바스크립트 코드와 다르게 list 함수가 하나 더 사용되었다. list 함수가 사용되지 않으면 map 함수는 계산을 하지 않는다. 자바스크립트는 계산된 배열을 돌려주지만 파이썬은 리스트를 돌려주지 않고 객체를 돌려준다.

    >>> # python
    >>> list(map(lambda x: x**2, [1, 3, 5, 7]))
    [1, 9, 25, 49]

map 함수에 전달되는 2번째 인자를 iterable 객체라고 하고 map 함수가 돌려주는 객체를 iterator라고 한다. iterator 객체는 내부에 __next__ 메서드를 가지고 있어서 내장 함수 next를 사용하면 순차적으로 데이터를 돌려준다. 모든 데이터를 돌려주면 StopIteration Exception이 발생한다. list 함수는 iterator 객체를 인자로 받아서 next 내장 함수를 사용하여 데이터를 수집한 후에 리스트 형식으로 돌려주는 것으로 볼 수 있다. Iterator 객체를 변수에 저장하여 활용하는 방식으로 코드를 변경해 보자.

    >>> # python
    >>> a = map(lambda x: x**2, [1, 3, 5, 7])
    >>> list(a)
    [1, 9, 25, 49]

a는 map 함수에 의하여 생성된 iternator 객체다. 이 객체는 iterator 객체를 인자로 받는 함수에 전달되어 그 값이 계산된다. 아래 코드를 보자

    >>> # python
    >>> a = map(lambda x: x**2, [1, 3, 5, 7])
    >>> list(a)
    [1, 9, 25, 49]
    >>> list(a)
    []

Iterator 객체는 한 번만 계산된다. 따라서 두 번 수행된 list 함수의 결과는 비어 있는 리스트이다. 비슷한 테스트로 Iterator 객체인 a 가 모든 값을 계산하지 않도록 for 문 중간에 멈추면 어떻게 될까? for 키워드도 list 함수와 마찬가지로 interator 객체로부터 값을 가져온다.

    >>> # python
    >>> a = map(lambda x: x**2, [1, 3, 5, 7])
    >>> for value in a:
    ...     print(value)
    ...     if value == (3*3):
    ...         break
    ...
    1
    9
    >>> list(a)
    [25, 49]

for 문 중간에 break 문으로 루프가 중지되면 a 객체는 더 이상 값을 돌려주지 않는다. 이후 list 함수를 다시 사용하면 a 객체는 나머지 값들을 돌려준다.

13. 지연된 계산

궁금한 것은 왜 map 함수는 바로 값을 돌려주지 않고 나중에 값들을 돌려주기 위하여 iterator 객체를 생성하는가? 자바스크립트처럼 호출 즉시 결과를 돌려주지 않는 것일까?

리스트 데이터 타입의 각각의 항목 값을 읽는 것을 iterate 한다고 한다. 이 경우 리스트 데이터 타입을 iterable 객체라고 하고 iterate 할 수 있도록 __next__ 메서드를 제공하는 객체를 iterator라고 한다. 이런 interator는 무한을 표현할 수 있으며 모든 리스트를 한 번에 읽지 않으며 언제든 멈출 수 있는 장점이 있다. 전체 리스트 값을 한 번에 계산하는 자바스크립트의 map 메서드와는 다르다. Iterator라는 계산 지연된 객체를 여러 목적에 맞는 함수에 전달할 수 있는 장점이 생긴다. 파이썬에서 iterator 객체는 __next__ 메서드를 구현하고 있음으로 next 내장 함수를 사용하여 값을 읽을 수 있다. 파이썬의 많은 함수들이 iterator를 인자로 사용하면서 공통의 인터페이스 공유하고 있다. 파이썬에서는 이런 선택이 가능하다.

이런 iterator 객체를 사용하는 방식은 자바스크립트에도 있다. Iterator 쉽게 만들어주는 generator를 만들 때 yield라는 키워드를 사용하는데 이런 방식도 자바스크립트에서 사용할 수 있다. 예제 코드를 확인할 수 있다.

답글 남기기

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

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.