Effective Python - 2장 정리

김세준·2021년 5월 6일

Hits

None 반환

  • 의미 있는 None 반환보다 예외를 raise 시키자.
def divide(a, b):
	try:
		return a / b
	except ZeroDivisionError as e:
		# return None  (X)
		raise ValueError('Invalid inputs') from e

클로저

def sort_prioriy(numbers, group):
	found = False  # scope -> sort_priorly()
	
	def helper(x):
		if x in group:
			found = True  # scope -> helper
			return (0, x)
		return (1, x)

	numbers.sort(key=helper)
	return found  # (X)
  • numbers.sort(key=helper) found의 값이 변해도 sort_prioriy() 에서는 found값이 바뀌지 않는다.
  • numbers.sort(key=helper) 에서 helper는 sort_prioriy() 의 스코프에 해당하는 변수들을 copy 해서 사용한다고 생각하면 편하다. 복사본의 값을 변경해도 원본의 값이 변하지 않는 것처럼 말이다.
def sort_prioriy(numbers, group):
	found = False  # scope -> sort_priorly()
	
	def helper(x):
		nonlocal found
		if x in group:
			found = True  # scope -> helper
			return (0, x)
		return (1, x)

	numbers.sort(key=helper)
	return found
  • nonlocal 문을 사용하여 클로저를 감싸는 있는 스코프의 변수를 수정할 수 있다.
  • 간단한 함수에만 사용하자.

제네레이터

def index_words(txt):
	result = []
	if txt:
		result.append(0)
	for index, letter in enumerate(txt):
		if letter = ' ':
			result.append(index + 1)
	return result
def index_words_iter(txt):
	if test:
		yield 0
	for index, letter in enumerate(txt):
		if letter == ' ':
			yield index + 1

result = list(index_words_iter(address))
  • 제네레이터는 이터레이터를 반환한다
  • 리스트를 반환할 때는 제네레이터를 고려하자

인수 순회 방어적으로 하기

def normalize(numbers: list):
	total = sum(numbers)
	for num in numbers:
		yield 100 * num / total

result = normalize([15, 35, 80])
print(list(result))
print(list(result))
# >>>
# [11.538461538461538, 26.923076923076923, 61.53846153846154]
# []
  • 제네레이터는 StopIteration 예외가 일어나면 결과 값을 저장하지 않는다.
    즉, 이터레이터로 결과를 한 번만 생성한다.
class Normalize:
    def __init__(self, numbers):
        self.numbers = numbers
    
    def __iter__(self):
        total = sum(self.numbers)
        for num in self.numbers:
            yield 100 * num / total

result = normalize([15, 35, 80])
print(list(result))
print(list(result))
# >>>
# [11.538461538461538, 26.923076923076923, 61.53846153846154]
# [11.538461538461538, 26.923076923076923, 61.53846153846154]
  • 이터레이터를 독립적으로 실행하면 입력 데이터를 여러번 읽는 단점만 빼면 기댓값을 얻을 수 있다.

가변 위치 인수

def log(message, *values):
	if not values:
		print(message)
	else:
		values_str = ', '.join(str(x) for x in values)
		print(f'{message} : {values_str}')

numbers = [21, 33, 31]
log("Hi")
log("My numbers are", 1, 2, 3)
log("My favorite numbers are", *numbers)

# >>>
# Hi
# My numbers are : 1, 2, 3
# My favorite numbers are : 21, 33, 31
  • 깔끔한 코드를 작성할 수 있다.
  • 가변 위치 변수는 함수로 전달되기에 앞서 튜플로 변환된다.
  • 호출 코드 수정 하기 전에는 위치 인수를 추가할 수 없다.

기본 인수를 None과 docstring 활용하기

키워드 전용 인수

def division(number, divisor, *, 
							ignore_overview=False,
							ignore_zero_division=False)
# ...

division(1, 10**500, True, False) # (X)
division(1, 0, ignore_zero_division=True) # (O)

0개의 댓글