diff --git a/example_code/item_05.py b/example_code/item_05.py index ec407db..241f204 100755 --- a/example_code/item_05.py +++ b/example_code/item_05.py @@ -77,11 +77,12 @@ def close_open_files(): print(f'Red: {red!r}') print(f'Green: {green!r}') print(f'Opacity: {opacity!r}') - +# get 메서드는 dict 안에 key가 없을 때 두 번째 인자 반환 +# in 사용하고 key가 없을 때 KeyError 처리하기보다 get 사용 # Example 5 red_str = my_values.get('red', ['']) -red = int(red_str[0]) if red_str[0] else 0 +red = int(red_str[0]) if red_str[0] else 0 # red_str = ['5'] green_str = my_values.get('green', ['']) green = int(green_str[0]) if green_str[0] else 0 opacity_str = my_values.get('opacity', ['']) @@ -89,7 +90,8 @@ def close_open_files(): print(f'Red: {red!r}') print(f'Green: {green!r}') print(f'Opacity: {opacity!r}') - +# or/and 사용하면 가독성이 떨어짐 +# if/else 조건문 사용 추천 # Example 6 green_str = my_values.get('green', ['']) diff --git a/example_code/item_06.py b/example_code/item_06.py index beafb39..0621d5f 100755 --- a/example_code/item_06.py +++ b/example_code/item_06.py @@ -54,14 +54,14 @@ def close_open_files(): } items = tuple(snack_calories.items()) print(items) - +# immutable 순서쌍을 만들어낼 수 있는 tuple (변경 불가능) # Example 2 item = ('Peanut butter', 'Jelly') first = item[0] second = item[1] print(first, 'and', second) - +# 인덱스를 이용한 접근 # Example 3 try: @@ -77,7 +77,7 @@ def close_open_files(): item = ('Peanut butter', 'Jelly') first, second = item # Unpacking print(first, 'and', second) - +# 인덱스보다 대입을 사용해 unpacking 추천 # Example 5 favorite_snacks = { @@ -115,8 +115,10 @@ def bubble_sort(a): for i in range(1, len(a)): if a[i] < a[i-1]: a[i-1], a[i] = a[i], a[i-1] # Swap + # unpacking을 이용한 swap names = ['pretzels', 'carrots', 'arugula', 'bacon'] + bubble_sort(names) print(names) @@ -131,5 +133,5 @@ def bubble_sort(a): # Example 9 -for rank, (name, calories) in enumerate(snacks, 1): - print(f'#{rank}: {name} has {calories} calories') +for rank, (name, calories) in enumerate(snacks, 1): # start=1로 주고 싶을 때 enumerate(, start=1) + print(f'#{rank}: {name} has {calories} calories') \ No newline at end of file diff --git a/example_code/item_07.py b/example_code/item_07.py index c1f4239..4ae9088 100755 --- a/example_code/item_07.py +++ b/example_code/item_07.py @@ -73,7 +73,8 @@ def close_open_files(): it = enumerate(flavor_list) print(next(it)) print(next(it)) - +# enumerate는 iterator를 지연 계산 제너레이터;lazy generator로 감싸있음 +# loop index와 iterator의 다음 값으로 이뤄씬 쌍 넘겨줌 (yeild) # Example 5 for i, flavor in enumerate(flavor_list): diff --git a/example_code/item_08.py b/example_code/item_08.py index c22151c..c45b49d 100755 --- a/example_code/item_08.py +++ b/example_code/item_08.py @@ -84,16 +84,19 @@ def close_open_files(): longest_name = name max_count = count assert longest_name == 'Cecilia' - +# zip은 둘 이상의 iterator를 지연 계산 제너레이터;lazy generator를 사용해 묶어줌 # Example 5 names.append('Rosalind') for name, count in zip(names, counts): print(name) - +# input iterator의 길이가 서로 다를 때 조심 +# ouput은 가장 짧은 input에 맞춰서 # Example 6 import itertools for name, count in itertools.zip_longest(names, counts): print(f'{name}: {count}') +# 길이가 같지 않다면 itertools.zip_longest 대신 사용 추천 +# zip_longest는 fillvalue를 사용해 빈 값을 채워줌 (기본값은 None) \ No newline at end of file diff --git a/example_code/item_09.py b/example_code/item_09.py index d96d033..45bf881 100755 --- a/example_code/item_09.py +++ b/example_code/item_09.py @@ -45,7 +45,7 @@ def close_open_files(): atexit.register(close_open_files) - +# for/while loop 뒤의 else block 사용하지 말라 # Example 1 for i in range(3): print('Loop', i) @@ -95,6 +95,8 @@ def coprime(a, b): if a % i == 0 and b % i == 0: return False return True +# 원하는 조건을 찾자마자 함수를 빠르게 return +# loop 빠져나갈 때 return 사용 assert coprime(4, 9) assert not coprime(3, 6) @@ -109,5 +111,8 @@ def coprime_alternate(a, b): break return is_coprime +# loop 안에서 원하는 대상을 찾았는지 나타내는 결과 변수 (is_coprime) 사용 +# 원하는 대상 찾자마자 break로 loop 탈출 + assert coprime_alternate(4, 9) assert not coprime_alternate(3, 6) diff --git a/example_code/item_10.py b/example_code/item_10.py index a106c38..181db30 100755 --- a/example_code/item_10.py +++ b/example_code/item_10.py @@ -45,7 +45,9 @@ def close_open_files(): atexit.register(close_open_files) - +# 대입식;assignment expression (왈러스 연산자;walrus operator) 사용해 반복 피하라 +# a = b (일반 대입문) +# a := b (대입식) a 왈러스 b # Example 1 fresh_fruit = { 'apple': 10, @@ -73,7 +75,7 @@ def out_of_stock(): make_lemonade(count) else: out_of_stock() - +# 한 줄 더 짧아지고, count가 if문의 첫 번째 block에서만 의미 있다는 점이 명확히 보임 # Example 4 def make_cider(count): @@ -87,6 +89,7 @@ def make_cider(count): # Example 5 +# 비교하기 위해 대입식 ()로 둘러싸기 if (count := fresh_fruit.get('apple', 0)) >= 4: make_cider(count) else: @@ -214,10 +217,11 @@ def make_juice(fruit, count): {'orange': 3, 'melon': 2}, ] +# 무한 루프-중간에서 끝내기;loop-and-a-half bottles = [] -while True: # Loop +while True: # Loop 무한 루프 fresh_fruit = pick_fruit() - if not fresh_fruit: # And a half + if not fresh_fruit: # And a half 중간에서 끝내기 break for fruit, count in fresh_fruit.items(): batch = make_juice(fruit, count) diff --git a/example_code/item_11.py b/example_code/item_11.py index f502f34..c89c683 100755 --- a/example_code/item_11.py +++ b/example_code/item_11.py @@ -103,24 +103,26 @@ def close_open_files(): print('After: ', b) print('No change:', a) +# 리스트 슬라이싱한 결과는 완전히 새로운 리스트 +# 원래 리스트에 대한 참조는 그대로 유지 +# 슬라이싱 리스트 변경해도 원래 리스트는 바꾸지 않음 # Example 9 print('Before ', a) -a[2:7] = [99, 22, 14] +a[2:7] = [99, 22, 14] # 슬라이스 대입 print('After ', a) # Example 10 print('Before ', a) -a[2:3] = [47, 11] +a[2:3] = [47, 11] # 슬라이스 대입에서는 언패킹 대입 (e.g. a, b = c[:2])과 달리 길이 같을 필요 X print('After ', a) # Example 11 -b = a[:] +b = a[:] # 원래 리스트 복사한 새 리스트 assert b == a and b is not a - # Example 12 b = a print('Before a', a) @@ -128,4 +130,4 @@ def close_open_files(): a[:] = [101, 102, 103] assert a is b # Still the same list object print('After a ', a) # Now has different contents -print('After b ', b) # Same list, so same contents as a +print('After b ', b) # Same list, so same contents as a (같은 리스트 객체라서 a와 내용이 같음) diff --git a/example_code/item_12.py b/example_code/item_12.py index 7744737..94dfa11 100755 --- a/example_code/item_12.py +++ b/example_code/item_12.py @@ -45,6 +45,7 @@ def close_open_files(): atexit.register(close_open_files) +# list[start:end:stride] # Example 1 x = ['red', 'orange', 'yellow', 'green', 'blue', 'purple'] @@ -55,9 +56,9 @@ def close_open_files(): # Example 2 -x = b'mongoose' -y = x[::-1] -print(y) +x = b'mongoose' +y = x[::-1] # 역으로 뒤집기 +print(y) # b'esoognom' # Example 3 @@ -97,3 +98,7 @@ def close_open_files(): print(x) print(y) print(z) + +# 한 slice 안에서 시작, 끝, 증가값을 함께 사용하지 말라 +# 세 parameter 모두 사용해야하는 경우, 두 번 대입 사용 (한 번은 striding, 한 번은 slicing) +# itertools.islice (시작, 끝, 증가값)에 음수 사용 불가능 diff --git a/example_code/item_13.py b/example_code/item_13.py index d2b9349..af134fa 100755 --- a/example_code/item_13.py +++ b/example_code/item_13.py @@ -67,6 +67,8 @@ def close_open_files(): # Example 3 oldest, second_oldest, *others = car_ages_descending print(oldest, second_oldest, others) +# index 사용하기보다 unpacking +# * (starred expression) 사용하면 모든 값을 담는 unpacking할 수 있도록 지원 # Example 4 @@ -86,6 +88,8 @@ def close_open_files(): logging.exception('Expected') else: assert False +# *others = car_ages_descending 같이 별표 식만 사용해 unpacking할 수 없음 +# 필수인 부분 적어도 하나 있어야 함 # Example 6 @@ -97,6 +101,8 @@ def close_open_files(): logging.exception('Expected') else: assert False +# first, *middle, *second_middle, last = [1, 2, 3, 4] 같이 +# 한 수준의 unpacking 패턴에서 별표 식 두 개 이상 사용할 수 없음 # Example 7 @@ -115,8 +121,8 @@ def close_open_files(): # Example 8 short_list = [1, 2] first, second, *rest = short_list -print(first, second, rest) - +print(first, second, rest) # 1 2 [] +# 별표 식은 항상 list instance를 반환하므로, rest는 빈 list가 됨 # Example 9 it = iter(range(1, 3)) diff --git a/example_code/item_14.py b/example_code/item_14.py index 271663a..e163c5e 100755 --- a/example_code/item_14.py +++ b/example_code/item_14.py @@ -110,7 +110,8 @@ def __repr__(self): saw = (5, 'circular saw') jackhammer = (40, 'jackhammer') assert not (jackhammer < saw) # Matches expectations - +# tuple (sort에 필요한 __lt__ 정의 들어가 있음) +# 각 index에 해당하는 원소를 한 번에 하나씩 비교 # Example 9 drill = (4, 'drill') @@ -118,10 +119,13 @@ def __repr__(self): assert drill[0] == sander[0] # Same weight assert drill[1] < sander[1] # Alphabetically less assert drill < sander # Thus, drill comes first +# 첫 번째 위치의 값이 서로 같으면 +# tuple의 비교 메서드는 두 번째 서로 비교하는 방식 +# 두 번째도 같으면 세 번째 비교 반복 # Example 10 -power_tools.sort(key=lambda x: (x.weight, x.name)) +power_tools.sort(key=lambda x: (x.weight, x.name)) # 먼저 weight로 정렬하고, 그 후 name으로 정렬 print(power_tools) @@ -132,7 +136,7 @@ def __repr__(self): # Example 12 -power_tools.sort(key=lambda x: (-x.weight, x.name)) +power_tools.sort(key=lambda x: (-x.weight, x.name)) # 숫자인 경우 - 연산자 사용해서 정렬 순서 혼합 가능 print(power_tools) @@ -164,3 +168,13 @@ def __repr__(self): power_tools.sort(key=lambda x: x.weight, reverse=True) print(power_tools) + +# key 함수에 tuple 반환하면 여러 정렬 기준을 하나로 엮을 수 있음 +# 단항 부호 반전 연산자 (-)를 사용하면 부호를 바꿀 수 있는 타입이 정렬 기준인 경우 +# 정렬 순서를 반대로 바꿀 수 있음 + +# 부호를 바꿀 수 없는 타입의 경우 여러 정렬 기준을 조합하려면 +# 각 정렬 기준마다 reverse 값으로 정렬 순서를 지정하면서 sort 메서드 여러 번 사용해야 함 +# 정렬 기준의 우선순위가 점점 높아지는 순서로 sort 호출해야함 +# e.g weight 내림차순 정렬 후 name에 의해 오름차순 정렬하고 싶으면 +# name 오름차순 정렬 후 weight 내림차순 정렬해야함 \ No newline at end of file diff --git a/example_code/item_15.py b/example_code/item_15.py index ef1ab90..2fb065e 100755 --- a/example_code/item_15.py +++ b/example_code/item_15.py @@ -59,7 +59,7 @@ def close_open_files(): print(list(baby_names.values())) print(list(baby_names.items())) print(baby_names.popitem()) # Last item inserted - +# popitem 마지막 itme 삭제하고, 반환 # Example 6 def my_func(**kwargs): @@ -79,6 +79,8 @@ def __init__(self): for key, value in a.__dict__.items(): print(f'{key} = {value}') +# 키 삽입과 popitem 호출을 매우 자주 처리해야한다면 +# least-recently-used (LRU) 표준 dict보다 OrderedDict를 사용하는 것이 좋다 # Example 9 votes = { @@ -91,7 +93,7 @@ def __init__(self): # Example 10 def populate_ranks(votes, ranks): names = list(votes.keys()) - names.sort(key=votes.get, reverse=True) + names.sort(key=votes.get, reverse=True) # 투표수 기준으로 오름차순 정렬 for i, name in enumerate(names, 1): ranks[name] = i @@ -111,7 +113,8 @@ def get_winner(ranks): # Example 13 from collections.abc import MutableMapping - +# collections.abc 모듈을 사용해 dict와 비슷하지만 +# 내용을 알파벳 순서대로 iteration해주는 클래스를 새로 정의할 수 있다 class SortedDict(MutableMapping): def __init__(self): self.data = {} diff --git a/example_code/item_16.py b/example_code/item_16.py index cf616ad..be2026b 100755 --- a/example_code/item_16.py +++ b/example_code/item_16.py @@ -178,6 +178,8 @@ def close_open_files(): print(votes) +# setdefault는 dict에서 key를 사용해 값을 가져오려고 시도하고 +# key가 없으면 default 값을 설정하고 반환한다 # Example 11 data = {} @@ -187,12 +189,17 @@ def close_open_files(): print('Before:', data) value.append('hello') print('After: ', data) - +# key가 없으면 setdefault에 전달된 디폴트 값이 별도로 복사되지 않고 딕셔너리에 직접 대입된다 +# key에 해당하는 default 값을 setdefault에 전달할 때마다 그 값을 새로 만들어야한다는 뜻 +# 호출할 때마다 리스트를 만들어야하므로 성능 저하될 수 있다 # Example 12 key = 'dutch crunch' -count = counters.setdefault(key, 0) +count = counters.setdefault(key, 0) # 여기서 setdefault 굳이 호출할 필요 없다 get 사용 counters[key] = count + 1 print(counters) + +# 문제에 dict의 setdefault 메섣 사용 방법이 가장 적합해보인다면 +# setdefault 대신 defaultdict 사용 고려해보자 \ No newline at end of file diff --git a/example_code/item_17.py b/example_code/item_17.py index b0e86ff..876ffb5 100755 --- a/example_code/item_17.py +++ b/example_code/item_17.py @@ -45,6 +45,8 @@ def close_open_files(): atexit.register(close_open_files) +# 내부 상태에서 원소가 없는 경우를 처리할 때는 +# setdefault보다 defaultdict를 사용하자 # Example 1 visits = { @@ -91,7 +93,7 @@ def __init__(self): self.data = defaultdict(set) def add(self, country, city): - self.data[country].add(city) + self.data[country].add(city) # set은 add() list는 append() visits = Visits() visits.add('England', 'Bath') diff --git a/example_code/item_18.py b/example_code/item_18.py index 2d9814f..63ddab7 100755 --- a/example_code/item_18.py +++ b/example_code/item_18.py @@ -45,6 +45,7 @@ def close_open_files(): atexit.register(close_open_files) +# __missing__을 사용해 key에 따라 다른 default값 생성하는 방법 알아두자 # Example 1 pictures = {} @@ -176,7 +177,6 @@ def open_picture(profile_path): except OSError: print(f'Failed to open path {profile_path}') raise - class Pictures(dict): def __missing__(self, key): value = open_picture(key) @@ -189,3 +189,9 @@ def __missing__(self, key): image_data = handle.read() print(pictures) print(image_data) + +# dict 타입의 하위 클래스를 만들고 +# __missing__ magic method를 구현하면 +# key가 없는 경우를 처리하는 로직을 커스텀화할 수 있다 +# 여기서는 open_picture 함수를 활용하는 새로운 Class를 정의해 +# key가 없는 경우 파일을 여는 dict 생성