사용법부터 보겠습니다
arr =[1, 2, 3]
numbers = [x for x in arr ]
>> numbers =[1, 2, 3]
numbers = [x for x in range(10)]
>> numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
첫 느낌은 짧다
짧다는 건 강점이 될수도 있지만, 지나친 숏코딩은 가독성이 떨어지게 된다
하지만 list comprehension의 경우 짧은것 이외에도 강점이 하나 더있다
밑에는 보편적으로 많이 사용되는 리스트를 만드는 방법이다
arr =[]
for i in range(10):
arr.append(i)
바로 위의 방식보다 걸리는 시간이 더 짧다
위에 보이는 시간이 일반적인 방식이 걸린 시간이고,
아래 보이는 시간이 list comprehension을 이용한 시간이다
함수를 호출해서 하는 방식은 이정도가 걸리는데 둘의 시간차이를 보면
2배 정도의 시간차이가 난다.
만약 함수를 호출하지 않고 바로 적는 방법은 얼마나 걸릴까 궁금했다
first_start = timeit.default_timer()
result = []
for i in range(1000):
result.append(i)
first_end = timeit.default_timer()
print(f"first_timer = {(first_end-first_start)*1000}")
second_start = timeit.default_timer()
numbers = [ i for i in range(1000)]
second_end = timeit.default_timer()
print(f"second_timer = {(second_end-second_start)*1000}")
이 두가지를 가지고 실험을 했는데 ,
결과는 list comprehension이 더 빠른걸로 나왓다
근데 여기서 함수를 불럿을때 처럼 거의 2배가량 차이 나는게 아닌
비슷한 경우도 있엇다
평균적으로 0.06 밀리세컨드 정도의 차이가 났다
혹시나 위에서 측정했던 방법이랑 달라서 차이가 나는건가 싶어서
모두 같은 방법으로 시간을 측정 해봣다
어떤 것이 더 빠르다 단정짓기가 어려운 문제인 것 같다
속도에 관한 글들을 찾아보니
어떤분은 함수로 호출할 경우가 더 빠르다 하고
또 어떤분은 바로 실행한 결과가 더 빠르다고 한다
아마 케바케이지 않을까 싶다
이제 제대로 이 두녀석에 대해 알아보겟습니다
둘의 가장 큰 차이점은 저장하느냐 안하느냐 입니다
이터레이터 안에 제너레이터가 포함되어 있는 것입니다
generator를 통해 iterator를 만든다,
혹은 iterator중에서도 특수한 경우에 generator를 사용한다
이 정도가 될 수 있습니다
우선 itertor 저장을한다 안한다로 나누었는데
iterator의 경우는 저장을 해놓고 하나씩 출력을 하는 방식입니다.
가장 기본적인 구조부터 보면
arr = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
iterator = iter(arr)
print(next(iterator))
print(next(iterator))
print(next(iterator))
위의 iterator의 경우는 next함수를 이용해서 값을 하나씩 출력하는 방법입니다
결과는 next를 입력했던 숫자만큼 출력이 되는 걸 확인할 수 있습니다.
같은 방법이긴 하나 아까랑은 조금다른
iterator를 메서드를 이용해서 사용하는 걸 보겠습니다.
사용법은 거의 비슷합니다
우선 코드부터 보면
arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
iterator = arr.__iter__()
count = 0
while count < 10:
print(iterator.__next__())
count += 1
위의 코드를 보게 되면 arr의 내부의 값을 출력하게 되는데
next 메서드를 이용해서 안의 값을 하나씩 출력하게 만들어 주는 코드입니다
결과를 보면
이렇게 정상적으로 출력이 되는걸 확인 할 수 있습니다.
저장이 된다 안된다는 마지막 generator까지 본다면 이해가 되실겁니다
모든 iterator는 iterable이 될 수 있지만,
모든 iterable이 iterator가 될 수는 없다
iterator = 반복 가능한 객체
while count<10 -> while count<11
일반적으로 리스트나, 튜플등 자기가 가지고 있는 범위를 벗어나는
범위를 설정하게 되면 IndexError가 발생합니다
근데 여기서 iterator의 경우는 다릅니다
이렇게 StopIteration 에러가 발생하며 종료하게 됩니다
우선 밑에 부터 보시죠
python에서 list의 dir입니다
보시면 iterator에서 소개한 next 메서드가 없는걸 확인 할 수 있는데
이는 list 뿐 아니라, set, dict, tuple, string 등... 반복가능한
iterable 객체인 것입니다
iter( list ) 를 사용하여 iterator 객체로 만들어 줄 수 있습니다
제너레이트의 경우는 저장을 하고 결과를 내놓는게 아닌
필요에 따라서 하나씩 결과물을 나오게 합니다
바로 yield를 사용해서 실행할 코드를 보내주는 겁니다
제너레이트의 경우는 인삿말을 붙이는 코드로 예를 들겠습니다
def hello(students):
for i in students:
yield (f"{i} Hello!")
student = ['junsik','minwoo','jiyoung','soheon']
hi = hello(student)
print(hi.__next__())
print(hi.__next__())
만약 이렇게 하게 된다면 어떻게 될까요?
list comprehension을 사용한 식은 바로 리스트를 출력해서 보여주고
generator expression을 사용한 식은 제너레이터 오브젝트로 출력을 합니다
iterator와 마찬가지로 next()메서드를 사용해서 호출해줍니다
제너레이터 표현식인데,
list comprehension과 사용법은 유사합니다
iterator = [i for i in range(10)] # list comprehension
generator = (i for i in range(10)) # generator expression
이와 같이 [] 대괄호가 아닌 () 소괄호를 사용해서
만들어 준다면 generator가 되는 겁니다
list comprehension과 generator expression의 차이점을 보면
이렇게 하나는 모두 계산된 상태로 출력을 해주고
다른 하나는 제너레이터 객체를 반환해 줍니다
모든 걸 저장해놓고 하나씩 출력하는게 아닌
필요에 의해 계산, 출력등의 행동을 보여주는데,
이를 Lazy evaluation 라고 합니다
실행을 지연시킨다는 뜻으로 generator가 사용되는 이유입니다
이제 제대로 둘의 차이점을 살펴 볼게요
import time
count = 1
def timer():
print("please wait for 1 second")
time.sleep(1)
print("return 1")
return 1
generator_list = (timer() for k in range(3))
for j in generator_list:
count -=1
print(j)
if count ==0:
break
우선 제너레이터 부터 살펴보면 계산을 전부 하지않고
그때그때 저장한다고 했는데
실행결과를 보게 되면
1개를 실행 시킨후 출력하는 걸 볼수 있습니다
다음은 list comprehension을 볼게요
import time
count = 1
def timer():
print("please wait for 1 second")
time.sleep(1)
print("return 1")
return 1
comprehension_list = [timer() for k in range(3)]
print("start comprehension")
for j in comprehension_list:
count -=1
print(j)
if count ==0:
break
실행 결과를 볼게요
우선 계산을 끝내고 난뒤에 밑에 for문을 실행시킨걸 확인 할 수 있습니다
이렇게 보면 확 와닿을 텐데
미리 계산할 결과를 저장해놓은 후에 하나씩 출력하는것과
미리 계산을 하지 않고 하나씩 출력하는걸 볼 수 있습니다
list comprehension의 경우는 미리 계산을 하기 때문에
계산한 만큼의 메모리를 할당 해놓았습니다
반면에 generator의 경우 계산을 하지 않은 만큼의 메모리 절약이 있습니다
둘의 차이점이 확실하게 보이는 결과인데
상황마다 다르겟지만 처리해야할 일이 많은데
모든걸 계산해놓고 출력을 하게되면 연산이 커질수록 그만큼
메모리를 할당하게 되고, 불필요한 메모리 할당이 이루어 지는겁니다
두가지의 차이점을 알아봤는데,
두가지 모두 사용하는 곳이 다르기 때문에 상황에 맞게
사용하시면 될것 같습니다
http://pythonstudy.xyz/python/article/23-Iterator%EC%99%80-Generator
두 곳모두 예시가 너무 좋아서 이해하는데
도움이 많이 됬습니다 감사합니다 😁