파이썬으로 향하는 고독하고 외로운 길…

Built-in types

기본 자료형 중 유용한 것들이나 의외의 것들에 대해 정리해 보자.

Set

set‘집합’으로, 수학에서 쓰이는 집합의 특징을 그대로 이용할 수 있다. 따라서 집합 연산 등을 처리할 때 사용할 수 있겠다. 보통은 여러 리스트에 대해 중복을 제거하거나(합집합), 교집합, 차집합 등을 구하기 위해 주로 사용하였다. 예를 들면 이런 식이다.

>>> a = [1,2,3,4,5]
>>> b = [3,4,5,6,7]
>>> list(set(a + b))
[1, 2, 3, 4, 5, 6, 7]
>>> list(set(a) | set(b))
[1, 2, 3, 4, 5, 6, 7]
>>> list(set(a) - set(b))
[1, 2]
>>> list(set(a) & set(b))
[3, 4, 5]
>>> list(set(a) ^ set(b))
[1, 2, 6, 7]

정리해 보면 다음과 같다.

set의 특징으로 순서가 없고, 중복이 없다. 따라서 멀쩡한 리스트를 set으로 변환했다가 다시 list로 변환하면 리스트 엘리먼트의 중복이 제거된다. 순서가 없다는 점이 문제가 된다면 다음과 같이 OrderedDict를 사용해서 처리할 수 있다.

...
>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys(a + b))
[1, 2, 3, 4, 5, 6, 7]

이는 dict를 생성할 때 그 키를 리스트의 엘리먼트를 이용해서 만드는 것으로, dict의 키가 중복이 되지 않기 때문에 가능하다. OrderedDict를 사용하는 이유는 순서를 지정해 주기 위해서인데, dict에는 순서가 없기 때문이다. 단순한 중복 제거가 목적이라면 dict로 사용해도 된다.

collections과 itertools

python에는 기본으로 제공되는 함수 중에 유용한 것들이 굉장히 많다. 읽어두면 이후에 반드시 한 번 이상 사용하게 되므로 읽어볼 가치가 충분하다.

Counter

Counter는 dict의 서브클래스이며, dict의 키를 사용해서 value를 계속 더해 나간다. 설명이 좀 애매한데 이런 방법으로 사용한다.

>>> from collections import Counter
>>> a = Counter({"first": 1, "third": 3})
>>> b = Counter({"first": 1, "second": 2, "third": 3})
>>> a + b
Counter({'third': 6, 'second': 2, 'first': 2})
>>> dict(a + b)
{'second': 2, 'first': 2, 'third': 6}

Counter를 쓰지 않으면 dict를 반복하면서 키를 찾고 이미 있는 키면 더하는 코드를 작성해야 하므로, Counter를 사용하여 간단히 처리하는 것이 매우 편하다.

DefaultDict

Counter와 마찬가지로 dict의 서브클래스로, 설명을 보면 거의 모든 것이 dict와 똑같고 두 가지만 바뀌었다고 쓰여 있는데 그 내용은 다음과 같다.

  • missing(key)
  • default_factory

dict에 값을 넣기 위해서는 어떤 키가 미리 있어야 하는데, dict의 키가 어떤 것이 될지 모르는 경우에 사용할 수 있다. 예를 들어 다음과 같이 사용할 수 있다.

>>> from collections import defaultdict
>>> a = defaultdict()
>>> a
defaultdict(None, {})
>>> a['1'] = 1
>>> a
defaultdict(None, {'1': 1})
>>> a['2'] = 2
>>> a['3'] = 3
>>> dict(a)
{'1': 1, '2': 2, '3': 3}

Conditional Expression

Conditional Expression, 조건부 표현식은 파이썬에서 정말 마음에 드는 문법 중 하나다. 이는 PEP-0308에 제안되어 있으므로 한번 살펴보자.

조건부 표현식을 사용하면 if-else를 한 줄에 쓸 수 있는데, 다른 언어에서 보통 ‘삼항 연산자’라고 불리는 것과 동일하다. 다만 문법이 약간 다르므로 여기서는 간단히 문법과 주의점 정도만 확인해 보자.

문법

<expression1> if <condition> else <expression2>

  1. condition을 평가하고,
  2. True라면 expression1을 전체의 결과로 사용한다.
  3. False라면 expression2를 전체의 결과로 사용한다.

만약 a가 True일 경우 Hello, False일 경우 World를 리턴하도록 conditional expression을 작성하는 경우

>>> "Hello" if a else "World"
'Hello'

주의할 점은 else가 무조건 포함되어야 한다는 것. else가 없다면 그냥 if a: 'Hello'로 쓰자.

List Comprehension

List Comprehension, 리스트 표현식은 간결하게 리스트를 만들 수 있도록 하는 파이썬의 문법이다. PEP 0202에 제안되어 있다.

문법

[ expression for item in list]

이걸 쓰지 않는 경우에는

>>> squares = []
>>> for x in range(10):
...     squares.append(x**2)
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

이와 같이 리스트를 작성해야 하지만,

>>> [ x**2 for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

훨씬 간결해짐을 알 수 있다.

또한 리스트 표현식을 if를 통해 필터링할 수 있는데, 문법은 다음과 같다.

[ expression for item in list if condition ]

예를 들어 결과 중 짝수만 필터링하려는 경우,

>>> [ x**2 for x in range(10) if x % 2 == 0]
[0, 4, 16, 36, 64]

리스트 표현식이 중첩되어 표현되거나(Nested) 혹은 조건 자체가 너무 복잡하게 나오는 경우 그냥 평범하게 쓰는 것만 못하므로 참고하자. 어차피 이 리스트 표현식이라는게 간단하게 리스트를 표현하자는 것이 목적일텐데, 굳이 매우 복잡한 결과를 내는 리스트 표현식을 억지로 한 줄에 쓰는 것도 웃기다고 생각한다.

Dict Comprehension

리스트 표현식과 같이 ‘Dict Comprehention’도 있다. 마찬가지로 딕셔너리를 간단하게 표현하기 위해 사용한다. 이는 PEP-0274에 제안되어 있다.

문법

{ k: v for k in (k,v) in sequence }

사실 이렇게 보면 이해가 잘 안가니 바로 예제를 보자.

>>> {i : chr(65+i) for i in range(4)}
{0: 'A', 1: 'B', 2: 'C', 3: 'D'}
>>> {q: q * 3 for q in range(10)}
{0: 0, 1: 3, 2: 6, 3: 9, 4: 12, 5: 15, 6: 18, 7: 21, 8: 24, 9: 27}

물론 리스트 표현식과 마찬가지로 if로 필터링을 주는 것이 가능하다.

>>> {q: q * 3 for q in range(10) if q % 2 == 0}
{0: 0, 2: 6, 4: 12, 6: 18, 8: 24}