파이썬 프로그래밍 기초 2부 1편

주요 내용

모음 자료형 1편

파이썬 프로그래밍 기초 2부에서 단일값 객체의 자료형인 스칼라 자료형을 살펴보았다. 여기서는 여러 개의 값으로 이루어진 객체의 자료형을 살펴본다.

여러 개의 값을 항목(item)으로 갖는 객체는 항목들을 다루는 방식에 따라 구분되며, 그런 객체의 자료형을 통틀어 모음(collection) 자료형이라 한다. 파이썬은 아래 모음 자료형들을 기본으로 제공된다.

이 중에 튜플과 리스트는 항목의 순서가 중요하다는 의미에서 순차 자료형(sequence)이라 불리기도 한다.

참고: 문자열(str)도 모음 자료형처럼 취급한다. 실제로 앞으로 살펴볼 인덱스, 인덱싱, 슬라이싱 등 순차 자료형과 관련된 기능을 문자열 또한 지원하기에 문자열을 순차 자료형으로 간주하기도 한다.

여기서는 튜플과, 사전에 대해 설명하며, 파이썬 프로그래밍 기초 4부에서 사전과 집합에 대해 설명한다.

튜플

튜플(tuple)은 여러 개의 값을 항목으로 가지며 소괄호로 감싼다.

굳이 소괄호를 사용하지 않아도 된다.

참고: 웬만하면 괄호를 사용하는 것이 혼동을 줄인다.

주의사항: 하나의 항목을 괄호를 감싼다 해도 튜플로 간주되지 않는다.

한 개의 항목으로 이루어진 튜플을 생성하려면 쉼표를 추가해야 한다. 하지만 이런 튜플은 굳이 사용할 이유가 별로 없다.

tuple() 함수는 다른 모음 자료형을 튜플로 변환한다.

중첩 튜플

튜플의 항목은 임의의 파이썬 객체가 사용될 수 있다. 즉, 튜플의 항목으로 튜플이 사용될 수 있다.

물론 항목으로 문자열, 리스트, 사전 등 임의의 값이 사용될 수 있다.

인덱스와 인덱싱

튜플 맨 왼편에 위치한 항목부터 차례대로 0, 1, 2, ... 로 시작하는 인덱스(index)를 갖는다. 인덱스를 이용하여 해당 위치의 항목을 확인할 수 있으며, 이를 인덱싱(indexing)이라 한다.

예를 들어 tup 변수가 가리키는 튜플의 첫째 항목은 s라는 사실을 아래와 같이 인덱싱으로 확인할 수 있다.

인덱스가 0부터 시작하기에 예를 들어 전체 항목의 수가 3이면 마지막 항목의 인덱스는 2이다.

튜플은 변경이 불가능한(immutable) 자료형이다. 예를 들어, 위 튜플의 마지막 항목을 인덱싱을 이용하여 False로 대체하고자 시도하면 오류가 발생한다.

참고: 아래 코드를 실행하면 TypeError 라는 오류가 발생하며, tuple 자료형은 항목 변경을 지원하지 않기 때문이라는 설명도 함께 보여진다.

튜플이 변경이 불가능한 자료형이라고 해서 튜플의 모든 항목이 모두 변경이 불가능학 객체이어야 하는 것은 아니다. 예를 들어 tup의 둘째 항목은 리스트 [1, 2] 인데, 리스트는 변경이 가능한(mutable) 자료형이다. 따라서 아래와 같이 둘째 항목 자체는 변경이 가능하다.

이런 성질이 가능한 이유를 아래 두 그림이 잘 설명한다.

tup('foo', [1, 2], True)를 참조한다. 그리고 둘째 항목인 [1, 2] 또한 참조 형태로 다른 메모리에 저장된다. 즉 tup의 둘째 항목은 [1, 2]가 저장된 위치의 주소이다. 그런데 [1, 2]가 변경되어도 주소 자체는 변하지 않는다. 따라서 tup 입장에서는 변한 게 하나도 없게 된다. 참고로, 리스트의 주소는 첫째 항목이 저장된 위치의 주소를 사용한다.

<변경 전>

<변경 후>

튜플 연산자

+ 연산자

두 개의 튜플을 이어붙인다.

튜플 여러 개를 이어붙일 수도 있다.

* 연산자

지정된 정수만큼 튜플을 복사해서 이어붙인다.

튜플 해체

튜플 항목 각각에 대해 변수를 지정하고자 할 때 튜플을 해체하는 기법을 사용한다. 단, 사용되는 변수의 수는 항목의 수와 일치해야 한다. 예를 들어, 세 개의 항목을 갖는 항목을 해체하려면 세 개의 변수가 필요하다.

변수 bc는 각각 둘째, 셋재 항목을 가리킨다.

굳이 이름을 주지 않아도 되는 항목이 있다면 변수 대신에 밑줄(underscore) 기호 _를 사용한다. 예를 들어 변수 a가 필요없다면 아래와 같이 튜플 해체를 해도 된다.

하지만 밑줄을 빼면 오류가 발생한다.

반면에 앞에 몇 개만 이름을 지정하고 나머지는 하나의 리스트로 묶을 수 있다. 이를 위해 별표 기호(asterisk) *를 하나의 변수이름과 함께 사용한다.

나머지 항목들을 무시하고 싶다면 별표와 밑줄을 함께 사용한다.

중첩 튜플을 해체할 때는 중첩 모양을 본딸 수도 있다. 예를 들어, 아래와 같이 하면 c는 셋째 항목의 첫째 항목을 가리킨다.

하지만 아래와 같이 하면 c는 튜플의 셋째 항목을 가리킨다.

활용 1

튜플 해체 방식을 활용하면, 여러 변수가 가리키는 값을 쉽게 바꿀 수 있다. 예를 들어, 변수 a, b가 각각 1과 2를 가리키도록 하자.

이제 아래와 같이 하면 a, b가 가리키는 값을 서로 바꾸게 된다.

활용 2

튜플 해체는 for 반복문에서 유용하게 사용된다. 즉, 리스트의 항목이 일정한 크기의 튜플일 때 각각의 항목에 변수를 지정하여 활용한다.

튜플 메서드

튜플은 변경이 불가능한 자료형이기에 제고되는 튜플 메서드가 많지 않으며, 특정 값이 항목으로 몇 번 사용되었가를 세어주는 count() 메서드와 특정 항목의 인덱스를 찾아주는 index() 메서드가 주로 사용된다.

예를 들어, 아래 리스트에서 숫자 2는 4번 사용되었다.

반면에 2가 가장 먼저 사용된 위치의 인덱스는 1이다.

리스트

리스트 사용법은 튜플과 유사하다.

list() 함수는 튜플, 문자열 등의 모음 자료형을 리스트로 변환한다.

항목 변경, 추가, 삭제

튜플과는 달리 리스트에 항목을 추가하거나, 특정 항목을 다른 항목으로 변경할 수 있으며, 리스트를 다루는 많은 메서드를 지원한다.

append() 메서드는 새로운 항목을 가장 오른편에 추가한다.

insert() 메서드는 지정된 인덱스 위치에 새로운 항목을 추가한다.

pop() 메서드는 지정된 인덱스 위치의 항목을 삭제한다. 그런데 단순히 삭제만 하는 것이 아니라 삭제되는 값을 반환한다.

인자를 지정하지 않으면 마지막 항목을 삭제한다.

remove() 메서드는 지정된 항목을 삭제한다. 지정된 항목이 여러 번 사용되었을 경우 가장 작은 인덱스의 값을 삭제한다.

in() 연산자는 특정 항목이 리스트에 포함되어 있는지 여부를 판단해준다.

리스트 이어붙이기

+ 연산자

두 개의 리스트를 이어붙여서 새로운 리스틀 생성한다.

extend() 메서드

주어진 리스트에 다른 지정된 리스트를 이어붙이는 방식으로 항목을 추가한다.

참고: 원래의 리스트를 수정하는 메서드이다. 항상 새로운 리스트를 생성하는 + 연산자보다 좀 더 빠르게 작동하며, 따라서 매우 긴 리스트를 이어붙일 때 기본적으로 선호된다.

리스트 정렬

sort() 메서드는 항목을 크기 순으로 정렬한다.

주의사항: sort() 메서드의 반환값은 None이다. 즉, 주어진 리스트의 항목을 크기 순으로 정렬하여 변경하지만 함수 자체의 반환값은 없다.

정렬할 때 사용되는 크기의 기준을 지정할 수 있다. 예를 들어, 문자열들을 기본값인 사전식 순서가 아니라 문자열들의 길이 기준으로 정렬하려면 항목의 크기를 계산하는 함수를 인자로 갖는 key 키워드의 인자를 len() 함수의 이름인 len으로 지정하면 된다.

참고로 key 키워드 인자를 지정하지 않은 알파벳 순서를 기준으로 삼는 사전식 순서로 정렬된다.

주의사항: 대문자가 소문자보다 작은 것으로 간주된다.

슬라이싱

슬라이싱 용법은 문자열, 튜플 등의 경우와 동일하다. 아래 코드는 1번부터 4번 인덱스의 값으로 이루어진 리스트를 생성한다.

위 그림에서 볼 수 있듯이 슬라이싱은 기존에 주어진 리스트를 수정하지 않으면서 구간 정보를 활용하여 새로운 리스트를 생성한다.

슬라이싱 기능을 이용하여 특정 위치부터 시작하는 구간에 여러 개의 항목을 추가할 수도 있다. 아래 코드는 3번, 4번 인덱스 위치의 값 대신에 4개의 원소를 추가 입력하는 것이다.

주의사항: 기존에 5번 이상의 인덱스에 위치한 값들은 더 추가되는 값들의 수 만큰 오른편으로 밀린다.

<변경 전>

<변경 후>

슬라이싱 구간의 시작과 끝을 지정하는 값을 필요에 따라 선택적으로 생략할 수도 있다. 생략된 값은 각각 리스트의 처음과 끝을 가리키는 값으로 처리된다. 아래 코드는 0번 인덱스부터 4번 인덱스까지의 구간을 대상으로 한다.

아래 코드는 3번 인덱스부터 리스트 오른편 끝가지를 대상으로 한다.

음수 인데스는 리스트 오른편 부터 -1, -2, -3, 등으로 왼편으로 이동하면서 지정된다. 아래 코드는 끝에서 4번째부터 마지막까지 구간을 대상으로 한다.

아래 코드는 끝에서 6번째부터 끝에서 두번째 이전, 즉, 끝에서 세번째까지 슬라이싱한다.

구간의 처음과 끝이 모두 생략되면 리스트 전체를 대상으로 한다. 아래 코드는 리스트 전체를 대상으로 하지만 2 스텝씩 건너 뛰며 항목을 슬라이싱한다. 즉, 0, 2, 4, ... 등의 인덱스를 대상으로 한다.

음수의 스텝이 사용되면 역순으로 슬라이싱된다. 아래 코드는 리스트의 오른편 끝에서 왼편으로 역순으로 슬라이싱한다. 즉, 기존의 리스트의 항목을 뒤집어서 새로운 리스트를 생성한다.

리스트의 마지막 항목의 인덱스는 아래 두 가지 방식으로 표현한다.

따라서 위 코드는 아래 코드와 동일하다.

아래 코드도 같다. 이유는 리스트의 길이가 10이기 때문이다.

range() 함수

규칙성을 가진 정수들의 모음(collection)을 반환한다. 반환된 값은 이터러블 객체이며, 리스트와 유사하게 작동한다.

참고: 이터러블 자료형에 대해서는 나중에 상세하게 다룬다.

예를 들어, 0부터 9까지의 정수들로 이루어진 range 객체는 다음과 같이 생성한다.

주의사항: 첫째 인자는 구간의 시작을, 둘째 인자는 구간의 끝보다 하나 큰 값을 가리킨다. 따라서 위에서 9가 아닌 그보다 1이 큰 10을 사용했음에 주의하라.

반환된 값의 자료형은 range 이다.

range(0, 10) 안에 포함된 항목을 for 반복문을 이용하여 확인할 수 있다.

리스트로 형변환을 하면 보다 명확하게 확인된다.

슬라이싱에서 처럼 스텝을 사용할 수 있다. 예를 들어, 0에서 19까지의 정수중에서 짝수만으로 이루어진 range 객체는 다음과 같이 스텝(step) 크기 2를 셋째 인자로 지정하여 생성한다.

스텝 크기를 음수로 지정하면 크기 역순으로 이루어진 range 객체를 생성한다.

주의사항: 음수 스텝을 사용할 경우 둘째 인자는 원하는 구간보다 1이 작은 값을 사용해야 한다.

range() 함수 주요 활용법 1

리스트 또는 튜플의 길이 정보를 이용하여 인덱싱을 활용하는 방식이 많이 사용된다.

range() 함수 주요 활용법 2

매우 많은 항목을 담은 리스트 대신에 range 객체를 for 반복문과 함께 사용한다. 이유는 range 객체가 리스트보다 훨씬 적은 메모리를 사용하기 때문이다. (이에 대한 근거는 여기서는 다루지 않는다.)

예를 들어, 아래 코드는 0부터 99,999 까지의 정수 중에서 3 또는 5의 배수를 모두 더한다.

range() 함수 주요 활용법 3

range() 함수와 list()는 서로 함께 잘 활용된다. 먼저, range() 함수를 이용하여 range 객체를 생성한 다음에 바로 리스트로 변환하면 리스트를 간단하게 구현할 수 있다.

순차 자료형에 유용한 함수

문자열, 튜플, 리스트처럼 항목들의 순서가 중요한 순차 자료형과 함께 유용하게 사용되는 네 개의 함수를 소개한다.

enumerate() 함수

튜플과 리스트의 인덱스를 튜플과 리스트 자체에서 눈으로 확인할 수 없다. 하지만 항목과 해당 항목의 인덱스 정보를 함께 활용해야 할 때가 있는데 이때 enumerate() 함수가 매우 유용하다.

enumerate() 함수는 리스트를 받아서 리스트의 항목과 인덱스를 쌍으로 갖는 모음 자료형의 객체를 준비시킨다. 이렇게 준비된 객체를 직접 확인할 수는 없다.

하지만 for 반복문을 이용하여 그 내용을 확인하고 활용할 수 있다. 예를 들어, 아래 코드는 짝수 인덱스의 값들만 출력하도록 한다.

주의사항: iv 두 변수를 활용하는 방식은 튜플 해체 방식이다.

아래 코드는 리스트의 항목을 키(key)로, 인덱스는 값(value)으로 하는 항목들로 이루어진 사전 자료형 객체를 생성한다.

sorted() 함수

sorted() 함수는 문자열, 튜플, 리스트의 항목을 크기 순으로 정렬시킨 리스트를 반환한다.

zip() 함수

문자열, 튜플, 리스트 여러 개의 항목을 순서대로 짝지어서 튜플의 리스트 형식의 객체를 생성한다. 단, zip() 함수의 반환값은 enumerate(), range() 함수처럼 구체적으로 명시해주지는 않는다.

하지만 리스트로 변환하면 쉽게 내용을 확인할 수 있다.

자료형이 달라도 되며, 각 자료형의 길이가 다르면 짧은 길이에 맞춰서 짝을 짓는다.

세 개 이상의 짝짓기도 가능하다.

활용법: enumerate() 처럼 for 반복문에 잘 활용된다. 아래 코드는 두 개의 리스트의 항목을 짝을 지은 후 인덱스와 함께 출력해준다.

고급 활용법: 동일한 인덱스에 위치한 항목들끼리 따로따로 모을 수 있다.

위 코드에서 사용된 별표(asterisk) 기호는 리스트를 해체하는 기능을 수행한다.

즉, pitchers가 아래 리스트를 가리킬 때

[('Nolan', 'Ryan'), ('Roger', 'Clemens'), ('Schilling', 'Curt')]

*pitchers는 다음 세 개의 튜플을 가리킨다.

('Nolan', 'Ryan'), ('Roger', 'Clemens'), ('Schilling', 'Curt')

따라서 zip(*pitchers)는 다음과 같은 함수의 호출이 된다.

reversed() 함수

순차 자료형의 항목을 역순으로 갖는 순차 자료형을 생성한다.