리스트 컴프리헨션을 사용하면 가독성이 좋아지는 장점도 있지만 일반적인 for문을 사용해서 리스트를 만들때보다 출력되는 속도가 빨라지는 장점이 있다.
cities = ["Tokyo", "Shanghai", "Jakarta", "Seoul", "Guangzhou", "Beijing", "Karachi", "Shenzhen", "Delhi" ]
cities_no_s = [ i for i in cities if i[0] != 'S']
print(cities_no_s)
population_of_city = [('Tokyo', 36923000), ('Shanghai', 34000000), ('Jakarta', 30000000), ('Seoul', 25514000), ('Guangzhou', 25000000), ('Beijing', 24900000), ('Karachi', 24300000), ( 'Shenzhen', 23300000), ('Delhi', 21753486) ]
dict_info_city = {population_of_city[i][0]:population_of_city[i][1] for i in range(len(population_of_city))}
print(dict_info_city)
기본적으로 리스트에는 iter 함수가 존재한다.
만약 l = [1,2,3] 이라는 리스트가 있다고 가정하고 dir(l)을 입력하면 아래와 같이 리스트에 어떠한 함수가 있는지 확인해 볼 수 있는데, iter가 목록에 있는것을 확인할 수 있다.
['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
__iter__() 함수를 출력해보면
print(l.__iter__())
<list_iterator object at 0x7fd0ed1c9f10>
이터레이터 객체임을 확인할 수 있다.
그리고 L2 = l.__iter__() 이터레이터를 변수에 담고 dir를 확인해보면
print(dir(L2))
['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']
print문으로 확인해보면 next 함수가 들어있는 것을 확인할 수 있다. __next__ 함수는 이름처럼 다음 요소를 하나씩 꺼내오는 함수이다.
__next__함수를 호출하면서 동작을 확인해보면, 리스트안에 1,2,3 3개의 값이 들어있으므로 3번이상 호출한다.
print(iterator_L.__next__())
print(iterator_L.__next__())
print(iterator_L.__next__())
print(iterator_L.__next__())
1,2,3 이 출력되고 StopIteration 이 발생하는 것을 확인할 수 있다. 즉, 리스트의 인덱스를 벗어나서 가져올 값이 없으면 StopIteration이 발생하는 것을 알 수 있다.
딕셔너리도 반복가능한 객체라서 앞서본 리스트와 같이 iter함수와 next함수를 사용할 수 있고 파이썬 기본함수인 iter, next 또한 사용할 수 있습니다. 다음의 간단한 키를 출력하는 딕셔너리에 대한 for 문을 while문으로 구현해 보세요.
D = {'a':1, 'b':2, 'c':3}
for key in D.keys():
print(key)
코드:
d = iter(D)
while True:
try:
i = next(d)
except StopIteration:
break
print(i, end=' ')
이렇게 while문으로 구현을 해보면 StopIteration이 발생 했을때 break 시켜주면 a b c 가 출력되는것을 확인할 수 있다.
파이썬에서 보통의 함수는 값을 반환하고 종료 하지만 제너레이터 함수는 값을 반환하기는 하지만 산출(yield)한다는 차이점이 있다. 그리고 제너레이터는 쉽게 얘기하면 이터레이터를 생성해주는 함수라고도 볼 수 있다.
다음 코드를 보면 함수안에서 yield를 사용하여 리스트의 제곱을 산출하는 함수가 있고, 이 함수를 print문으로 확인해보면 generator object 임을 확인할 수 있다.
아래의 코드를 분석해보겠습니다.
L = [ 1,2,3] 가 있다고 가정해보면,
comprehension_list=
sleep 1s
sleep 1s
sleep 1s
1
2
3
generator_exp=
sleep 1s
1
sleep 1s
2
sleep 1s
3
두개의 출력값이 다르다는것을 확인할 수 있는데 컴프리헨션 리스트인 경우에는 lasy_return이 3번 출력되면서 return 되는 num을 리스트에 저장시킨 후 print_iter 함수를 통해서 한번에 출력된다.
반면에 제너레이터는 값을 저장하지 않고 한번씩 번갈아 가면서 출력되는것을 볼 수 있다. 쉽게 말하면, lazy_return 을 통해서 1이 출력이 되면 바로 print_iter에 1을 대입시켜 출력하고 다시 lazy_return 값을 출력시켜 2의 값을 바로 print_iter 함수에 대입해 한번씩 번갈아 가면서 값을 출력 시킨다. 이 반복을 리스트 길이 만큼 반복한다.
위에서 처럼, 리스트 컴프리헨션을 사용했을때와 제너레이터를 사용했을때의 메모리 사용값을 확인해보면 제너레이터는 실제 데이터를 만드는것이 아니기 때문에 10개, 100개, 1000개의 값들을 넣어도 리스트 컴프리헨션과 달리 메모리 사용이 증가하지 않는것을 확인할 수 있다.
람다는 인라인 함수를 정의할 때 사용하며 익명 함수(anonymous functions) 또는 람다 표현식(lambda expression)이라고 부른다.
람다 표현식을 잘 사용하면 코드가 깔끔해지는 장점이 있다.
람다의 기본 문법:
lambdas = [lambda x: "SHORT_PASSWORD" if len(x) < 8 else None,
lambda x: "NO_CAPITAL_LETTER_PASSWORD" if not any(c.isupper()
for c in x) else None]
이렇게 람다 식으로 코드를 작성하면 print값이 잘 나온다.
SHORT_PASSWORD
NO_CAPITAL_LETTER_PASSWORD
True