for문을 돌거나 다른 반복 처리중 보이는 마지막 몇가지 항목을 유지하고 싶다면 어떻게 할까?


그렇다면 collections 모듈에 deque를 import해서 사용하면 된다.


아래의 코드는 특정 텍스트를 인자로 던져주면 해당 특정 텍스트를 파일에서 찾게되면,


해당라인의 전 history 라인만큼 리턴해주는 Python 코드이다


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)

# Example use on a file
if __name__ == '__main__':
    with open('somefile.txt') as f:
        for line, prevlines in search(f, 'python', 5):
            for pline in prevlines:
                print(pline, end='')
            print(line, end='')
            print('-'*20)

어떠한 item을 검색하기 위한 코드를 작성할때는 yield를 쓰는것이 일반적이다.


Posted by C마노
,

길이를 알수 없는 요소를 Unpacking 하고 싶다면?


이럴때는 *를 사용하는데, 예를 들면, 리스트중 첫번째와 마지막을 버리고 중간으로만 평균을 낸다.

라고 가정을 하면 어떻게 만들수 있을까?


def drop_first_last(grades):
    first, *middle, last = grades
    return avg(middle)

굉장히 간단하게 구현이 가능하다.


또한 아래와 같은 경우에도 사용이 가능하다


[ '이름', '이메일', '전화번호', '전화번호', '전화번호' ]


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

위의 코드에서 phone_number는 항상 list라는 것이 포인트다

전화번호의 개수에 상관없이 unpacking이 가능하다.


*된 변수는 제일 마지막이라는 법은 없다. 처음이 될 수도 있다.


1~8 분기 까지의 판매실적이 있는데 1~7까지만 평균을 보고 8분기는 현재실적을 리턴하고 싶다면 아래와 같이 구현할수 있다


*trailing_qtrs, current_qtr = sales_record
trailing_avg = sum(trailing_qtrs) / len(trailing_qtrs)
return avg_comparison(trailing_avg, current_qtr)

파이썬 입장에서 해석되는 코드 (아래)


>>> *trailing, current = [10, 8, 7, 1, 9, 5, 10, 3]
>>> trailing
[10, 8, 7, 1, 9, 5, 10]
>>> current
3

*을 사용하면 길이가 분명하지 않은 반복가능한 객체에 대해서 값을 출력할수 있음.


활용을하자면 다양한 길이의 튜플이 존재할 경우 아래와 같이 활용할 수있음.


records = [
     ('two', 1, 2),
     ('one', 'hello'),
     ('two', 3, 4),
]

def do_foo(x, y):
    print('two', x, y)

def do_bar(s):
    print('one', s)

for tag, *args in records:
    if tag == 'two':
        do_foo(*args)
    elif tag == 'one':
do_bar(*args)

*사용하면 split 함수와도 굉장히 잘 어울립니다.!


>>> line = 'nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false'
>>> uname, *fields, homedir, sh = line.split(':')
>>> uname
'nobody'
>>> homedir
'/var/empty'
>>> sh
'/usr/bin/false'
>>>

또한 아래와 방법으로도 사용이 가능하다. (이부분에서 좀 놀랐음.)


record = ('ACME', 50, 123.45, (12, 18, 2012)) 의 데이터가 있으면

이름과 마지막 2012의 년도만 값으로 얻고싶으면 어떻게할까?


>>> record = ('ACME', 50, 123.45, (12, 18, 2012))
>>> name, *_, (*_, year) = record
>>> name
'ACME'
>>> year
2012
>>>

위와같이 사용하면 된다.


첫값을 가지고 오고, 나머지는 리스트로 반환하려면?


>>> items = [1, 10, 7, 4, 5, 9]
>>> head, *tail = items
>>> head
1
>>> tail
[10, 7, 4, 5, 9]
>>>

위의 방법을 활용하면 '효율적인 재귀알고리즘'을 만들 수 있다.


>>> def sum(items):
...     head, *tail = items
...     return head + sum(tail) if tail else head
...
>>> sum(items)
36
>>>

그렇지만 추천 하는 방법은 아님. 그냥 이런 방법이 있구나 하고 알아두자.

Posted by C마노
,
>>> p = (4, 5)
>>> x, y = p
>>> x
4
>>> y
5
>>>

>>> 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
>>>

요소수에 불일치가 있으면 에러가 난다는 것에 주의하자.


>>> p = (4, 5)
>>> x, y, z = p
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: need more than 2 values to unpack
>>>

변수에 대한 Unpacking은 튜플이나 리스트에 국한되는것이 아님.

여기는 문자열, 파일 및 이터레이터와 제너레이터가 포함됨.


>>> s = 'Hello'
>>> a, b, c, d, e = s
>>> a
'H'
>>> b
'e'
>>> e
'o'
>>>


unpacking을 시도중 분명히 버리고 싶은 값이 있을 수도 있음.

그럴때는 _를 사용해서 버려주면 된다.


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






Posted by C마노
,