1. Data Structures and Algorithms

1.1. Unpacking a Sequence into Separate Variables

P : N개 원소로 이루어진 튜플이나 순열을 N개의 변수로 언패킹하고 싶다.

S : 단순 대입 연산으로 가능하다. 개수가 맞지 않으면 에러가 난다. 튜플/리스트만 아니라 문자열/파일/이터레이터/제너레이터 등 순회 가능한 오브젝트는 전부 가능하다. 언패킹하면서 어떤 변수를 버리고 싶을 때는 _ 등의 더미 변수명을 사용하면 된다. 단 해당 변수명이 이미 쓰이고 있지는 않은지 주의하라.

>>> data = [ 'ACME', 50, 91.1, (2012, 12, 21) ]
>>> name, shares, price, date = data
>>> name
'ACME'
>>> date
(2012, 12, 21)

>>> name, shares, price, (year, mon, day) = data
>>> name
'ACME'
>>> year
2012
>>> mon
12
>>> day
21

1.2. Unpacking Elements from Iterables of Arbitrary Length

P : 임의 길이의 순회 가능한 오브젝트로부터 N개의 원소만 언패킹하고 싶다.

S : * 표현을 쓰면 된다. 이 때 *로 받은 변수는 항상 리스트가 된다. 가변 길이 튜플의 시퀀스를 받을 때 유용하다. 문자열 분할 등과도 연계해 사용할 수 있다.

>>> record = ('Dave', 'dave@example.com', '773-555-1212', '847-555-1212')
>>> name, email, *phone_numbers = record
>>> name
'Dave'
>>> email
'dave@example.com'
>>> phone_numbers
['773-555-1212', '847-555-1212']
>>>

1.3. Keeping the last N items

P : 순회한 오브젝트 중 마지막 N개만 유지하고 싶다.

S : collection.deque를 쓰면 된다. 항목을 찾는 코드를 작성할 때에는 yield를 포함한 제너레이터 함수를 만들면 좋다.

from collections import deque

def search(lines, pattern, history=5):
    previous_lines = deque(maxlen=history)
    for line in lines:
        if pattern in line:
            yield line, previous_lines
        previous_lines.append(line)

1.4. Finding a Largest or Smallest N items

P : 컬렉션에서 가장 크거나 작은 N개의 원소를 찾고 싶다.

S : heapq의 nlargest(), nsmallest()를 쓰면 된다. 이 때 복잡한 자료 구조에 대해 활용하도록 키 파라미터를 받을 수 있다. heap[0]은 최소 원소가 되며 heappop() 메소드로 첫 아이템을 pop하고 다음 아이템으로 치환할 수 있다. 이 때 시간복잡도는 O(log M)이 된다. N의 크기가 컬렉션 크기와 커지면 우선 컬렉션을 정렬한 뒤 그 조각을 사용하는 것이 빠르다.

import heapq

nums = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]
print(heapq.nlargest(3, nums))  # Prints [42, 37, 23]
print(heapq.nsmallest(3, nums)) # Prints [-4, 1, 2]

1.5. Implementing a Priority Queue

P : 우선순위에 따라 항목을 정렬하는 큐를 만들고 항상 우선순위가 높은 아이템을 팝하게 하고 싶다.

S : heapq를 쓰면 된다. 이 때 큐는 튜플 형태로 구현한다. 우선순위가 동일한 아이템 사이의 순서를 정할 때는 self._index를 사용한다.

import heapq

class PriorityQueue:
    def __init__(self):
        self._queue = []
        self._index = 0

    def push(self, item, priority):
        heapq.heappush(self._queue, (-priority, self._index, item))
        self._index += 1

    def pop(self):
        return heapq.heappop(self._queue)[-1]

1.6. Mapping Keys to Multiple Values in a Dictionary

P : 딕셔너리의 키를 하나 이상의 값의 매핑하고 싶다 (“multidict”라 불린다)

S : 키에 매핑하기 전에 그 여러 값을 리스트나 세트 등에 따로 저장해두면 된다. collections.defaultdict를 사용하면 첫 번째 값을 초기화할 때의 번거로운 과정을 없앨 수 있다. 다만 이 경우 딕셔너리에 존재하지 않는 값이라도 한 번이라도 접근하는 것만으로 키의 엔트리를 빈 값으로 자동으로 생성한다. 이것이 마음에 들지 않으면 일반 딕셔너리의 setdefault()를 사용한다.

from collections import defaultdict

d = defaultdict(list)
d['a'].append(1)
d['a'].append(2)
d['b'].append(4)
...

d = defaultdict(set)
d['a'].add(1)
d['a'].add(2)
d['b'].add(4)
...

1.7. Keeping Dictionaries in Order

P : 딕셔너리를 만들고, 순회나 직렬화할 시의 순서를 조정하고 싶다.

S : colletions.OrderedDict를 사용한다. 삽입 초기의 순서를 그대로 따르게 된다. OrderedDict는 이중 연결 리스트로 구현되어 있기 때문에 메모리 소모가 크다.

from collections import OrderedDict

d = OrderedDict()
d['foo'] = 1
d['bar'] = 2
d['spam'] = 3
d['grok'] = 4

# Outputs "foo 1", "bar 2", "spam 3", "grok 4"
for key in d:
    print(key, d[key])

1.8. Calculating with Dictionaries

P : 딕셔너리 데이터에 대해 최소값/최소값/정렬 등의 계산을 수행하고 싶다.

S : 키에 대한 계산을 수행하려면 그냥 min(), max() 등을 쓰면 된다. 값에 대한 계산을 수행하려면 .values()을 순회하거나, zip()으로 딕셔너리의 키와 값을 뒤집은 이터레이터를 생성해서 활용할 수 있다. 최소/최대값에 대응되는 키를 찾고 싶다면 min()과 max()에 람다로 키를 제공하면 된다. 동일한 값을 가지고 있다면 비교는 키를 통해 이루어진다.

prices = {
   'ACME': 45.23,
   'AAPL': 612.78,
   'IBM': 205.55,
   'HPQ': 37.20,
   'FB': 10.75
}

min_price = min(zip(prices.values(), prices.keys()))
# min_price is (10.75, 'FB')

max_price = max(zip(prices.values(), prices.keys()))
# max_price is (612.78, 'AAPL')

1.9. Finding Commonalities in Two Dictionaries

P : 두 딕셔너리에서 유사점을 찾고 싶다. (같은 키, 같은 값 등)

S : 집합 연산인 &, -, | 등을 활용하면 된다. values()는 딕셔너리 내의 값이 유일하지 않을 수 있기 때문에 해당 집합 연산을 지원하지 않으나, 원한다면 values() 셋을 집합으로 변환한 뒤에 사용하면 된다.

a = {
   'x' : 1,
   'y' : 2,
   'z' : 3
}

b = {
   'w' : 10,
   'x' : 11,
   'y' : 2
}

# Find keys in common
a.keys() & b.keys()   # { 'x', 'y' }

# Find keys in a that are not in b
a.keys() - b.keys()   # { 'z' }

# Find (key,value) pairs in common
a.items() & b.items() # { ('y', 2) }

1.10. Removing Duplicates from a Sequence while Maintaining Order

P : 시퀀스에서 순서는 유지하면서 중복된 값을 없애고 싶다.

S : 그냥 셋으로 만든다면 중복은 없어지지만 순서가 깨진다. 순서를 유지하고 싶다면 해시 가능한 값들이라면 셋과 제너레이터를 이용해 해결할 수 있다. 해시 불가능하다면 (딕셔너리 등) key 인자를 추가해서 해결한다.

def dedupe(items):
    seen = set()
    for item in items:
        if item not in seen:
            yield item
            seen.add(item)

1.11. Naming a Slice

P : 하드코딩된 슬라이스 인덱스가 많아 이를 정리하고 싶다.

S : 슬라이스에 이름을 붙이면 된다.

######    0123456789012345678901234567890123456789012345678901234567890'
record = '....................100          .......513.25     ..........'
cost = int(record[SHARES]) * float(record[PRICE]

1.12. Determining the Most Frequently Occurring Items in a Sequence

P : 시퀀스에 가장 많이 나타난 아이템을 찾고 싶다.

S : collections.Counter의 most_common() 메소드를 쓰면 된다. collections.Counter는 덧셈, 뺄셈도 지원한다.

words = [
   'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes',
   'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around', 'the',
   'eyes', "don't", 'look', 'around', 'the', 'eyes', 'look', 'into',
   'my', 'eyes', "you're", 'under'
]

from collections import Counter
word_counts = Counter(words)
top_three = word_counts.most_common(3)
print(top_three)
# Outputs [('eyes', 8), ('the', 5), ('look', 4)]

1.13. Sorting a List of Dictionaries by a Common Key

P : 딕셔너리 리스트가 있고, 하나 이상의 딕셔너리 값으로 이를 정렬하고 싶다.

S : operator.itemgetter 를 사용하면 된다. 여러 인덱스를 전달할 수도 있고, 람다 표현식으로 대체할 수도 있다. 정렬뿐만 아니라 min(), max() 함수에도 사용할 수 있다.

rows = [
    {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003},
    {'fname': 'David', 'lname': 'Beazley', 'uid': 1002},
    {'fname': 'John', 'lname': 'Cleese', 'uid': 1001},
    {'fname': 'Big', 'lname': 'Jones', 'uid': 1004}
]
from operator import itemgetter

rows_by_fname = sorted(rows, key=itemgetter('fname'))
rows_by_uid = sorted(rows, key=itemgetter('uid'))

print(rows_by_fname)
print(rows_by_uid)

rows_by_lfname = sorted(rows, key=itemgetter('lname','fname'))
print(rows_by_lfname)

1.14. Sorting Objects without Naive Comparison Support

P : 기본적 비교 연산이 없는 클래스들을 정렬하고 싶다.

S : __init__에서 정렬하고 싶은 기반 요소를 attribute에 입력한 뒤 람다식이나 operator.attrgetter()를 사용하면 된다. 역시 정렬뿐만 아니라 최대/최소에도 쓸 수 있다.

class User:
    def __init__(self, user_id):
        self.user_id = user_id
    def __repr__(self):
        return 'User({})'.format(self.user_id)

users = [User(23), User(3), User(99)]
sorted(users, key=lambda u: u.user_id)
from operator import attrgetter
sorted(users, key=attrgetter('user_id'))

1.15. Grouping Records Together Based on a Field

P : 딕셔너리나 인스턴스에서 특정 필드 값을 기준으로 그룹핑해 순회하고 싶다.

S : 먼저 해당 필드를 기준으로 정렬해야 한다. 그 뒤 itertools.groupby() 함수를 쓰면 된다. 정렬하고 싶지 않다면 필드를 기준으로 defaultdict()를 사용해서 multidict를 구성해서 쓰면 된다.

rows = [
    {'address': '5412 N CLARK', 'date': '07/01/2012'},
    {'address': '5148 N CLARK', 'date': '07/04/2012'},
    {'address': '5800 E 58TH', 'date': '07/02/2012'},
    {'address': '2122 N CLARK', 'date': '07/03/2012'},
    {'address': '5645 N RAVENSWOOD', 'date': '07/02/2012'},
    {'address': '1060 W ADDISON', 'date': '07/02/2012'},
    {'address': '4801 N BROADWAY', 'date': '07/01/2012'},
    {'address': '1039 W GRANVILLE', 'date': '07/04/2012'},
]
from operator import itemgetter
from itertools import groupby

# Sort by the desired field first
rows.sort(key=itemgetter('date'))

# Iterate in groups
for date, items in groupby(rows, key=itemgetter('date')):
    print(date)
    for i in items:
        print('    ', i)

1.16. Filtering Sequence Elements

P : 시퀀스 내 데이터를 추출하거나 특정 조건에 따라 시퀀스를 줄이고 싶다.

S : 리스트 컴프리헨션이나 생성자 표현식을 쓸 수 있다. 아니면 필터링 함수를 filter() 안에 넣어서 사용하면 된다. boolean 선택자를 itertools.compress()에 전달해 쓸 수도 있다. filter()나 compress()는 반복자를 생성하므로 결과를 리스트로 만들고 싶다면 list()를 써야 한다.

mylist = [1, 4, -5, 10, -7, 2, 3, -1]
[n for n in mylist if n > 0]
pos = (n for n in mylist if n > 0)

values = ['1', '2', '-3', '-', '4', 'N/A', '5']

def is_int(val):
    try:
        x = int(val)
        return True
    except ValueError:
        return False

ivals = list(filter(is_int, values))
print(ivals)
# Outputs ['1', '2', '-3', '4', '5']
from itertools import compress
more5 = [n > 5 for n in counts]
list(compress(addresses, more5))
# ['5800 E 58TH', '4801 N BROADWAY', '1039 W GRANVILLE']

1.17. Extracting a Subset of a Dictionary

P : 딕셔너리의 부분을 또 다른 딕셔너리로 만들고 싶다.

S : 딕셔너리 컴프리헨션을 쓰면 된다. 이는 튜플 시퀀스를 dict()에 전달하는 것으로도 가능하다.

prices = {
   'ACME': 45.23,
   'AAPL': 612.78,
   'IBM': 205.55,
   'HPQ': 37.20,
   'FB': 10.75
}

# Make a dictionary of all prices over 200
p1 = { key:value for key, value in prices.items() if value > 200 }

# Make a dictionary of tech stocks
tech_names = { 'AAPL', 'IBM', 'HPQ', 'MSFT' }
p2 = { key:value for key,value in prices.items() if key in tech_names }

p1 = dict((key, value) for key, value in prices.items() if value > 200)

1.18. Mapping Names to Sequence Elements

P : 리스트나 튜플의 위치로 요소에 접근하는 코드에 유연성을 주고 싶다.

S : collections.namedtuple()을 사용하면 된다. namedtuple은 딕셔너리 대신 사용할 수 있으나 변경이 불가능하다. 속성을 수정하고 싶으면 _replace() 메소드를 만드는데 이는 지정한 값을 치환해 새로운 네임드 튜플을 만든다. 여러 요소를 자주 수정해아 한다면 namedtupe은 쓰지 않는 것이 좋다.

from collections import namedtuple
Subscriber = namedtuple('Subscriber', ['addr', 'joined'])
sub = Subscriber('jonesy@example.com', '2012-10-19')

1.19. Transforming and Reducing Data at the Same Time

P : 데이터를 변환하거나 필터링한 뒤 sum(), min(), max() 등을 실행하고 싶다.

S : 생성자 표현식을 쓰면 된다.

nums = [1, 2, 3, 4, 5]
s = sum(x * x for x in nums)

1.20. Combining Multiple Mappings into a Single Mapping

P : 여러 개의 딕셔너리나 매핑을 하나의 매핑으로 합치고 싶다.

S : collections.ChainMap을 사용하면 된다. 이 때 ChainMap은 매핑을 실제로 합치는 것은 아니며 리스트를 유지하면서 순회 시의 딕셔너리 동작을 재정의할 뿐이다. 중복 키가 있으면 첫 번째 매핑의 값을 사용한다. 매핑의 값을 변경하는 동작은 첫 번째 매핑에 영향을 준다.

a = {'x': 1, 'z': 3 }
b = {'y': 2, 'z': 4 }
from collections import ChainMap
c = ChainMap(a,b)
print(c['x'])      # Outputs 1  (from a)
print(c['y'])      # Outputs 2  (from b)
print(c['z'])      # Outputs 3  (from a)

답글 남기기

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

WordPress.com 로고

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

Google photo

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

Twitter 사진

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

Facebook 사진

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

%s에 연결하는 중