부스트캠프에서 새롭게 알게 된 사실들

cmder

파이썬

1) Counter 객체

Counter 객체는 딕셔너리 자료형 처럼 키와 값이 있지만 집합(set) 자료형에서 가능한 연산을 사용할 수 있다.

from collections import Counter

dic1 = ['a', 'b', 'a', 'd', 'c', 'c']
dic2 = ['a', 'b', 'k', 'c', 'c', 'c', 'z', 'z']

c1 = Counter(dic1)
c2 = Counter(dic2)

print(c1, c2, sep='\n')

print(c1 | c2)
print(c1 & c2)
print(c1 - c2)
-------------------------------------------------
Counter({'a': 2, 'c': 2, 'b': 1, 'd': 1}) # c1
Counter({'c': 3, 'z': 2, 'a': 1, 'b': 1, 'k': 1}) # c2

Counter({'c': 3, 'a': 2, 'z': 2, 'b': 1, 'd': 1, 'k': 1}) # c1, c2의 합집합 -> 중복되는 키는 큰 값 기준
Counter({'c': 2, 'a': 1, 'b': 1}) # c1, c2의 교집합 -> 중복되는 키값만, 작은 값 기준
Counter({'a': 1, 'd': 1}) # c1, c2의 차집합 -> c1이 가진 키 기준으로 c1과 중복되는 c2의 (키에 해당하는) 값이 더 크면 값이 0이 되는 것이 아니라 아예 키-값 쌍이 사라짐  

2) reduce

from functools import reduce

print(reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]))

print(list(reduce(lambda x, y: x+y, [[1, 2], [3, 4], [5, 6], [7, 8]])))
---------------------------------------------------------
15
[1, 2, 3, 4, 5, 6, 7, 8]

3) defaultdict

나는 그동안 자료형만 default로 할 줄 알았는데, 값을 default로 줄 수도 있었다.

from collections import defaultdict

# 자료형을 default
dict1 = defaultdict(int)

# 값을 default -> 유의할 점은 바로 '가나다' 이렇게 주지 않고 함수를 리턴하는 방식으로 지정
dict2 = defaultdict(lambda: '가나다')

dict1['a']
dict2['b']

print(dict1, dict2, sep='\n')
----------------------------------------
defaultdict(<class 'int'>, {'a': 0})
defaultdict(<function <lambda> at 0x000001F7701F71F0>, {'b': '가나다'})

4) 이터레이터와 제너레이터

이터레이터

iterable한 객체를 iteration하기 위해서는 iterable한 객체의 iterator를 호출(iter()내장함수 이용) 하여, iterator가 값을 반복적으로 호출하도록(next()내장함수 이용) 하면 된다. 어떤 클래스를 Iterable 하게 하려면, 그 클래스의 iterator를 리턴하는 __iter__() 메서드를 작성해야 한다. 실제 for 루프에 Iterable Object를 사용하면, 해당 Iterable의 __iter__() 메서드를 호출하여 iterator를 가져온 후 그 iterator의 next() 메서드를 호출하여 루프를 돌게 된다.

alpha = ['A', 'B', 'C']

for i in alpha:
    print(i)

alpha = iter(alpha)
for i in range(3):
    print(next(alpha))
import sys

# iterable한 객체
alpha = ['A', 'B', 'C']

print(sys.getsizeof(alpha)) # 64 , 72, 80bytes, ...
print(dir(alpha)) # __iter__는 있으나 __next__는 없다

# iterator
alpha = iter(alpha)

print(sys.getsizeof(alpha)) # 48, 48, 48bytes, ...
print(dir(alpha)) # __iter__, __next__를 가지고 있다

print(next(alpha)) # A
print(alpha.__next__()) # B

제너레이터

Generator는 Iterator의 특수한 한 형태이다. 따라서 제너레이터도 이터레이터와 마찬가지로 메모리를 효율적으로 사용한다.
Generator 함수(Generator function)는 함수 안에 yield를 사용하여 데이터를 하나씩 리턴하는 함수이다. Generator 함수가 처음 호출되면, 그 함수 실행 중 처음으로 만나는 yield에서 값을 리턴한다. Generator 함수가 다시 호출되면, 직전에 실행되었던 yield 문 다음부터 다음 yield 문을 만날 때까지 문장들을 실행하게 된다. 이러한 Generator 함수를 변수에 할당하면 그 변수는 generator 클래스 객체가 된다.

리스트나 Set과 같은 컬렉션에 대한 iterator는 해당 컬렉션이 이미 모든 값을 가지고 있는 경우이나, Generator는 모든 데이터를 갖지 않은 상태에서 yield에 의해 하나씩만 데이터를 만들어 가져온다는 차이점이 있다. 이러한 Generator는 데이터가 무제한이어서 모든 데이터를 리턴할 수 없는 경우나, 데이터가 대량이어서 일부씩 처리하는 것이 필요한 경우, 혹은 모든 데이터를 미리 계산하면 속도가 느려서 그때 그때 On Demand로 처리하는 것이 좋은 경우 등에 종종 사용된다.

  • 모든 제너레이터는 이터레이터이다
  • 모든 제너레이터는 게으른 팩토리이다 (즉, 값을 그 때 그 때 생성한다)
def generator_list(value):
    result = []
    for i in value:
        yield i
from itertools import islice
def fib():
    prev, curr = 0, 1
    while True:
        yield curr
        prev, curr = curr, curr + prev

f =fib()
print(list(islice(f, 0, 10)))
-----------------------------------------
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

🔔 islice :
islice(iterable객체, [시작], 정지[,step])의 함수로, iterable한 객체를 특정 범위로 슬라이싱하고 iterator로 반환됩니다.

참고 자료

부스트캠프 강의 참조
예제로 배우는 파이썬 프로그래밍 참조
mingrammer’s note 참조

5) 가변인자

*(asterisk)를 사용하면 함수의 인자를 가변적으로 사용할 수 있다

위치 가변 인자

def test1(name, values, *details):
    if details:
        print(f'이름: {name}, 수량: {values}, 설명: {details}')
    else:
        print(f'이름: {name}, 수량: {values}')

test1("apple", 5, '원산지: 국산', '가격: 5000원' )
test1("pineapple", 2, '원산지: 미국산')
test1("lemon", 3)
--------------------------------------------------
이름: apple, 수량: 5, 설명: ('원산지: 국산', '가격: 5000원')
이름: pineapple, 수량: 2, 설명: ('원산지: 미국산',)
이름: lemon, 수량: 3

키워드 가변 인자

def print_parameters(**kwargs):
  for key, value in kwargs.items():
    print(f'{key} = {value}')

print_parameters(apple=5, banana=10)
------------------------------------
apple = 5
banana = 10
def print_parameters(**kwargs):
  for key, value in kwargs.items():
    print(f'{key} = {value}')

dic = {'apple': 5, 'banana': 10}

print_parameters(**dic)
------------------------------------
apple = 5
banana = 10

가변인자와 zip

ex = ([1, 2], [3, 4], [5, 6], [7, 8], [9, 10])

for value in zip(ex):
  print(value)
  # 기대하는 결과
  # [1, 3, 5, 7, 9]
  # [2, 4, 6, 8, 10]
---------------------------------
([1, 2],)
([3, 4],)
([5, 6],)
([7, 8],)
([9, 10],)

# ex는 리스트를 원소로 담는 튜플 한 개 이므로, ex의 원소 [1, 2] 한 개와 빈 값, ex의 원소 [3, 4]와 빈 값 이런식으로 출력된다
# 따라서 기대하는 결과값을 얻기 위해서는 ex가 zip안에서 언패킹되어야 한다
ex = ([1, 2], [3, 4], [5, 6], [7, 8], [9, 10])

for value in zip(*ex): # 바뀐 부분
  print(value)
-----------------------------------------------
(1, 3, 5, 7, 9)
(2, 4, 6, 8, 10)

Tags:

Categories:

Updated: