본문 바로가기

Python/Intermediate

[Python] 병행성(Concurrency)(2) - generator

# 병렬성(Parallelism) : 여러 컴퓨터가 여러 작업을 동시에 수행 -> 속도

# 병행성(Concurrency) : 한 컴퓨터가 여러일을 동시에 수행
#  -> 단일 프로그램 안에서 여러일을 쉽게 해결
# 이터레이터, 제네레이터
# Iterator, Generator

# 파이썬 반복 가능한 타입
# for, collections, text file, List, Dict, Set, Tuple, unpacking, *args

 

Iterator, Generator - 값을 차례대로 가져올 수 있음

 

✅ Iterator (이터레이터)

"하나씩 값을 꺼낼 수 있는 객체"

  • __iter__()와 __next__() 메서드를 가진 객체
  • for 문, next() 함수와 함께 사용 가능
  • 값을 직접 기억하고 관리함 (내부 상태)

 

✅ Generator (제너레이터)

"yield로 값을 순차적으로 만들어내는 특별한 이터레이터"

  • yield 키워드로 값을 하나씩 생성 (return 아님)
  • 자동으로 __iter__()와 __next__() 구현됨
  • 메모리를 덜 쓰고, 필요한 순간에만 값을 생성 (lazy)

 

 


# Generator Ex1
def generator_ex1():
    print('Start')
    yield 'A Point.'
    print('continue')
    yield 'B Point.'
    print('End')

temp = iter(generator_ex1())

print(next(temp))
# Start
# A Point.
print(next(temp))
# continue
# B Point.

# print(next(temp)) # exception
# # End
# # Traceback (most recent call last):
# #     print(next(temp))
# # StopIteration
for v in generator_ex1(): # ==> for 문은 예외처리를 해줌.
    print(v)

--------------------------------------------[result]

start
A_Point
continue
B_Point
end

 

 

Comprehenstion & Generator 생성

  • Comprehenstion 리스트는 생성시 결과가 메모리에 저장되므로 리스트 생성과 동시에 print문이 동시에 출력됨
  • Generator 는 생성시 메모리 할당이 발생하지 않아서 print문이 출력되지 않음, next 호출시 출력됨

 

# Generator Ex2
temp2 = [x * 3 for x in generator_ex1()] # 여기서 start, continue, end 출력 발생
# Start
# continue
# End
temp3 = (x * 3 for x in generator_ex1())

print(type(temp2))
print(type(temp3))
# < class 'list' >
# < class 'generator' >

print(temp2)
print(temp3)
# ['A Point.A Point.A Point.', 'B Point.B Point.B Point.']
# <generator object <genexpr> at 0x7facd8194b30>


for i in temp2: # yield 만 저장되어 출력됨
    print(i)
# A Point.A Point.A Point.
# B Point.B Point.B Point.

for i in temp3:
    print(i)
# Start
# A Point.A Point.A Point.
# continue
# B Point.B Point.B Point.
# End

for i in temp3:
    print(i)
# 더 이상 출력이 되지 않는다, 제너레이터는 한번만 사용가능.

 

 

Generator 메모리 처리 샘플 코드

 

def square_numbers(nums):
    for i in nums:
        yield i * i
 
my_nums = square_numbers(([1,2,3,4,5]))
print(my_nums, type(my_nums))
# < generator object square_numbers at 0x000002B832000CA8 > < class 'generator' >

for num in my_nums: # 주석처리하면 b 로 시작하는 for 문이 출력됨.
    print('a', num)
# a 1
# a 4
# a 9
# a 16
# a 25

for num in my_nums: # 메모리가 비어 있어서 출력되는게 없음.
    print('b', num)

 

 

itertools 관련 패키지 모음 - count, takewhile, filterfalse, accumulate, chain, product, groupby

 

import itertools

# 무한 반복문
gen1 = itertools.count(1, 2.5)
print(type(gen1))
print(gen1)
# < class 'itertools.count' >
# count(1, 2.5)

print(next(gen1))
print(next(gen1))
print(next(gen1))
print(next(gen1))
# 1
# 3.5
# 6.0
# 8.5
# # ... 무한

--------------------------------------------
--------------------------------------------

# 무한 반복문, 조건 추가
gen2 = itertools.takewhile(lambda n : n <= 11, itertools.count(1, 2.5))
print(type(gen2))
print(gen2)
print(next(gen2))
# < class 'itertools.takewhile' >
# < itertools.takewhile object at 0x000002B831FF9E48 >
# 1

for v in gen2:
    print(v)
# 1
# 3.5
# 6.0
# 8.5    

--------------------------------------------
--------------------------------------------

# 필터 반대, 1,2 제외하고 출력함. 'n>=3' 과 동일한 효과
gen3 = itertools.filterfalse(lambda n : n < 3, [1,2,3,4,5])

for v in gen3:
    print(v)
--------------------------------------------[result]
3
4
5

--------------------------------------------
--------------------------------------------

# 필터처리
gen3 = filter(lambda n : n < 3, [1,2,3,4,5])

for v in gen3:
    print(v)
--------------------------------------------[result]
1
2

--------------------------------------------
--------------------------------------------

# 누적 합계
gen4 = itertools.accumulate([x for x in range(1, 11)])

for v in gen4:
    print(v)
--------------------------------------------[result]
1
3
6
10
15
21
28
36
45
55

--------------------------------------------
--------------------------------------------

# 연결1
gen5 = itertools.chain('ABCDE', range(1,11,2))
print(gen5)
print(list(gen5))

# 연결2
gen6 = itertools.chain(enumerate('ABCDE'))
# 1번만 실행됨.

print(dict(gen6)) # 둘중 한곳에서만 출력됨
print(list(gen6)) # 둘중 한곳에서만 출력됨
--------------------------------------------[result]
< itertools.chain object at 0x000002B831F63C18 >
['A', 'B', 'C', 'D', 'E', 1, 3, 5, 7, 9]
{0: 'A', 1: 'B', 2: 'C', 3: 'D', 4: 'E'}
[]

--------------------------------------------
--------------------------------------------

# 개별, tuple 의 리스트 형식으로 반환
gen7 = itertools.product('ABCDE')
print(gen7)

print(list(gen7))

# 연산(경우의 수), 2개씩 경우의 수를 표현
gen8 = itertools.product('ABCDE', repeat=2)

# print(len(list(gen8)))
print(list(gen8))
print('*****************************')
print(list(gen8))
--------------------------------------------[result]
< itertools.product object at 0x000002B83200E828 >
[('A',), ('B',), ('C',), ('D',), ('E',)]
[('A', 'A'), ('A', 'B'), ('A', 'C'), ('A', 'D'), ('A', 'E'), ('B', 'A'), ('B', 'B'), ('B', 'C'), ('B', 'D'), ('B', 'E'), ('C', 'A'), ('C', 'B'), ('C', 'C'), ('C', 'D'), ('C', 'E'), ('D', 'A'), ('D', 'B'), ('D', 'C'), ('D', 'D'), ('D', 'E'), ('E', 'A'), ('E', 'B'), ('E', 'C'), ('E', 'D'), ('E', 'E')]
*****************************
[]

--------------------------------------------
--------------------------------------------

# 그룹화
gen9 = itertools.groupby('AAABBCCCCDDEEE')

# print(list(gen9))

# for chr, group in gen9: # 둘중에 한곳만 출력됨
#     print(chr, ' : ', list(group))
# A  :  ['A', 'A', 'A']
# B  :  ['B', 'B']
# C  :  ['C', 'C', 'C', 'C']
# D  :  ['D', 'D']
# E  :  ['E', 'E', 'E']

print('----------')
for chr, group in gen9: # 둘중에 한곳만 출력됨
    print(chr, ' : ', len(list(group)))    
--------------------------------------------[result]
----------
A  :  3
B  :  2
C  :  4
D  :  2
E  :  3