파이썬에서는 병행성과 병렬성을 지원합니다.
여기서 병행성이란 한 컴퓨터가 여러일을 동시에 수행하는 것을 의미합니다. 즉 하나의 프로세스에서 여러일을 쉽게 해결하는 것입니다.
반면에 병렬성이란 여러 프로세스가 여러작업을 동시에 수행하는 것을 의미합니다. 예컨대 파이썬으로 크롤링을 할때 여러사이트에 동시에 접속해서 각각 정보를 수집하는 것 등입니다. 이러한 병렬성은 일반적으로 속도상승에 목적을 두고 있습니다.
파이썬에서 병행성을 이해하기 위해서는 제너레이터를 알아야합니다.
제너레이터의 개념은 아래 페이지를 참고하시면 됩니다.
https://zero-week.tistory.com/151
또한 반복가능한 객체에 대한 이해도 필요합니다.
1. iter의 이해
1) 반복가능한 객체의 내부동작 원리
파이썬에서 반복가능한(iterable) 타입에는 여러가지가 있지만 문자열로 예제를 만들어보겠습니다.
txt = "abcd"
for c in txt:
print("for문:",c)
#출력:
# for문: a
# for문: b
# for문: c
# for문: d
일반적으로 문자열을 반복할때는 위 코드처럼 표시합니다.
이것은 문자열이 반복가능한 타입이기 때문에 가능한 코드입니다. 이 코드는 내부적으로는 아래처럼 실행됩니다.
w1 = iter(txt)
while True:
try:
print("while문:",next(w1))
except StopIteration:
break
#출력: -> iter 가능한 객체가 내부 동작하는 원리
# while문: a
# while문: b
# while문: c
# while문: d
2) 반복 가능한지 확인하는 방법
객체가 반복가능한지 확인하는 방법에는 아래 3가지 방법이 있습니다.
print(dir(txt)) #-> __iter__를 찾는다 -> 있으면 반복 가능
print(hasattr(txt,'__iter__')) # True
print(isinstance(txt,abc.Iterable)) # True
3) 반복가능한 객체의 __next__메서드와 __iter__ 메서드 사용 비교
# next 패턴
class WordSplitter:
def __init__(self,txt):
self._idx = 0
self._txt = txt.split(' ')
def __next__(self):
print("next 메서드 호출")
try:
word = self._txt[self._idx]
except IndexError:
raise StopIteration("반복 중단.")
self._idx += 1
return word
def __repr__(self):
return f'WordSplit({self._txt})'
w1 = WordSplitter("오늘 날씨는 맑습니다.")
print(w1) #출력: WordSplit(['오늘', '날씨는', '맑습니다.'])
print(next(w1))
#출력:
# next 메서드 호출
# 오늘
print(next(w1))
#출력:
# next 메서드 호출
# 날씨는
print(next(w1))
#출력:
# next 메서드 호출
# 맑습니다.
# 제너레이터 패턴
# 1. 지능형 리스트, 딕셔너리, 집합 생성 후 데이터 양이 증가할때 메모리 사용량도 증가 -> 제너레이터 사용권장(메모리 절약)
# 2. 단위 실행 가능한 코루틴 구현과 연동 가능
# 3. 작은 메모리 조각을 사용
class WordSplitterGenerator:
def __init__(self,txt):
self._txt = txt.split(' ')
def __iter__(self):
print("__iter__ 메서드 호출")
for word in self._txt:
yield word # 제너레이터
def __repr__(self):
return f'WordSplitGenerator({self._txt})'
wg1 = WordSplitterGenerator("오늘 날씨는 맑습니다.")
wt = iter(wg1)
print(wt) #출력: <generator object WordSplitterGenerator.__iter__ at 0x000001DF1C268B30>
print(wg1) #출력: WordSplitGenerator(['오늘', '날씨는', '맑습니다.'])
print(next(wt))
#출력:
# __iter__ 메서드 호출
# 오늘
print(next(wt)) #출력: 날씨는
print(next(wt)) #출력: 맑습니다.
2. iter에서의 제너레이터
yield 는 제너레이터에서 사용되며 return의 역할을 합니다. 그러나 return과 다른점은 함수에서 return을 사용하면 함수가 종료되기 때문에 return을 두개 이상 사용할 수 없습니다. 또한 return에서 반복가능한 객체를 반환하면 첫번째 값만 반환됩니다. yield 는 이와 다르게 하나의 함수내에서 여러번 사용 가능하며 반복가능한 객체 전체를 반환합니다.
예제)
def return_test():
print("aa")
return "bcded" # b만 출력됨
print("cc") # 출력안됨
return "dd" # 출력안됨
rt = iter(return_test())
print(next(rt))
#출력:
# aa
# b
def generator_ex1():
print("start")
yield "A point" # return의 역할 -> 크롤링 함수를 넣는것도 가능
print("continue")
yield "B point"
print("end")
temp = iter(generator_ex1())
print(next(temp))
#출력:
# start
# A point
print(next(temp))
#출력:
# continue
# B point
for v in generator_ex1():
print(v)
#출력:
# start
# A point
# continue
# B point
# end
temp2 = [x*3 for x in generator_ex1()]
temp3 = (x*3 for x in generator_ex1())
print(temp2) #출력: ['A pointA pointA point', 'B pointB pointB point']
print(temp3) # 출력: ['A pointA pointA point', 'B pointB pointB point']
for i in temp3:
print(i)
# 출력:
# start
# A pointA pointA point
# continue
# B pointB pointB point
# end
1) 제너레이터 함수
# filterfalse, takewhile, accumulate, chain, product, groupby..
import itertools
gen1 = itertools.count(1,2.5) # 무한대의 반복가능한 객체를 만드는 함수. 시작값은 1에서 2.5씩 더해서 무한대수를 만들어줌 -> while 사용하면 안됨
print(next(gen1)) #출력: 1
print(next(gen1)) #출력: 3.5
print(next(gen1)) #출력: 6.0
print(next(gen1)) #출력: 8.5
print(next(gen1)) #출력: 11.0
# 조건
gen2 = itertools.takewhile(lambda n : n < 50,itertools.count(1,5)) # takewhile은 제한을 거는 함수
for v in gen2:
print(v,end=" ") #출력: 1 6 11 16 21 26 31 36 41 46
print()
gen3 = itertools.filterfalse(lambda n:n<3,[1,2,3,4,5]) #filterfalse는 필터의 반대역할을 하는 함수 -> false만 출력
for v in gen3:
print(v)
#출력:
# 3
# 4
# 5
#누적 합계
gen4 = itertools.accumulate([x for x in range(1,11)])
for v in gen4:
print(v,end=" ") #출력: 1 3 6 10 15 21 28 36 45 55
print()
# 연결
gen5 = itertools.chain('abcde',range(1,5)) # chain은 서로 다른 두개의 iterable 객체를 연결하는 함수
print(list(gen5)) # 출력: ['a', 'b', 'c', 'd', 'e', 1, 2, 3, 4]
gen6 = itertools.chain(enumerate('ABCDE')) # 인덱스와 값을 튜플형태로 연결함
print(list(gen6)) #출력: [(0, 'A'), (1, 'B'), (2, 'C'), (3, 'D'), (4, 'E')]
# 순서쌍(경우의 수)을 만들어주는 함수
gen7 = itertools.product("abcde")
print(list(gen7)) #출력: [('a',), ('b',), ('c',), ('d',), ('e',)]
gen8 = itertools.product("abc",repeat=3) # repeat은 순서쌍의 개수
print(list(gen8))
#출력:
# [('a', 'a', 'a'), ('a', 'a', 'b'), ('a', 'a', 'c'), ('a', 'b', 'a'), ('a', 'b', 'b'),
# ('a', 'b', 'c'), ('a', 'c', 'a'), ('a', 'c', 'b'), ('a', 'c', 'c'), ('b', 'a', 'a'),
# ('b', 'a', 'b'), ('b', 'a', 'c'), ('b', 'b', 'a'), ('b', 'b', 'b'), ('b', 'b', 'c'),
# ('b', 'c', 'a'), ('b', 'c', 'b'), ('b', 'c', 'c'), ('c', 'a', 'a'), ('c', 'a', 'b'),
# ('c', 'a', 'c'), ('c', 'b', 'a'), ('c', 'b', 'b'), ('c', 'b', 'c'), ('c', 'c', 'a'),
# ('c', 'c', 'b'), ('c', 'c', 'c')]
# 그룹화
gen9 = itertools.groupby("aaabbbcccddeee")
for chr, group in gen9:
print(chr, ":", list(group))
#출력
# a : ['a', 'a', 'a']
# b : ['b', 'b', 'b']
# c : ['c', 'c', 'c']
# d : ['d', 'd']
# e : ['e', 'e', 'e']
'PYTHON Programming > Python' 카테고리의 다른 글
[Python] 파이썬 병렬 프로그래밍 - futures (0) | 2024.07.25 |
---|---|
[Python] 파이썬 동시성- 코루틴 (2) | 2024.07.25 |
[Python] 파이썬 시퀀스 (0) | 2024.07.25 |
[Python] 일급함수- 장식자(데코레이터) (0) | 2024.05.28 |
[python] 일급함수 - 클로저(Closure) (0) | 2024.05.28 |