python

이펙티브 파이썬 2nd 정리 #1

기기디 2023. 7. 28. 22:21

5. 복잡한 식을 쓰는 대신 도우미 함수를 작성하라

✅ 다른 언어에서는 처리하기 위해 if문을 사용하거나 별도 함수를 선언하여 처리할 수 있다. 하지만 파이썬에서는 이런 방식보다는 if expression을 사용하는게 깔끔하다.

빈 문자열, 빈 리스트, 0은 모두 False로 인식된다.

red = my_values.get('빨강',[''])[0] or 0
green = my_values.get('초록',[''])[0] or 0
opacity = my_value.get('투명도', [''])[0] or 0

✅ 조건을 판단하는 등 반복되는 로직이 있다면 별도의 함수를 작성해서 옮겨라. 비록 두세 번 밖에 사용되지 않더라도 작성하는걸 권장한다.

def get_first_int(values, key, defalut=0):
	found = value.get(key, [''])
    if found[0]:
    	return int(found[0])
    return default

✅ 불(boolean) 연산자 or나 and를 식에 사용하는 것보다 if/else 식을 쓰는 편이 더 가독성이 좋다.

red = int(my_value.get("빨강",[''])[0] or 0)

위와 같은 코드는 다른 사람이 이해하기 어렵고, 시각적으로 보기 좋지 않다. 해당 로직을 새로 읽는 사람 입장에서 이렇게 작성된 코드가 있다면 이해하고 작업하기까지 시간 투자가 필요하다. 코드를 짧게 한줄로 작성하는건 좋지만 모든 내용을 한줄에 우겨 넣기 위해서 한줄로 작성 할 필요는 없다.

✅ 가독성 좋고 이해하기 쉽게 작성하는게 더 가치있다.

red_str = my_values.get("빨강",[''])
red = int(red_str[0]) if red_str[0] else 0

이런식으로 if/else 문을 사용하는게 분명하고 가독성이 좋다. 코드를 무조건 줄여 쓰는 것보다 가독성을 좋게 하는게 더 가치 있다. 파이썬의 함축적인 문법이 오히려 지저분한 코드를 만들지 않도록 주의해야한다.

7. range보다는 enumerate를 사용하라

✅ range 사용시 문제

파이썬에서 제공하는 range내장 함수는 정수 집합, 문자열로 이루어진 list를 이터레이션 할 때 쉽게 사용할 수 있다.

종종 list를 이터레이션 할 때 리스트의 인덱스에 따라서 처리가 필요한 경우가 있다. 이때 range로 이터레이션을 사용하면 list의 길이를 알아야하고 인덱스를 사용해서 리스트를 접근해야하기 때문에 가독성이 떨어진다

for i in range(len(dummy_list)):
    dummy = dummys[i]
    print(f"{i+1} : {dummy}")

✅ enumerate

이런 경우 enumrate를 사용한는것이 권장된다

for idx, dummy in enumerate(dummys):
    print(f'{idx+1} : {dummy}')

10. 대입식을 사용해서 반복을 피하라

✅ 바다코끼리 연산자(왈러스 연산자)

대입식은 바다코끼리 연산자 혹은 왈러스 연산자(walrus operator)라고도 부른다. 대입식은 파이썬에서 코드 중복 문제를 개선하고자 파이썬 3.8에서 새롭게 도입된 구문이다. 바다코끼리 연산자는 a := b라고 쓰며 ‘a 왈러스 b’라고 읽는다(‘왈러스’라는 이름은 :=이 바다코끼리(walrus)의 눈과 엄니처럼 보이기 때문에 붙여졌다고 한다).

왈러스 연산자 도입 이전에는 값을 가져와서 0이 아닌지 검사한 후 사용하는 패턴이 자주 발생했다. 이런 패턴을 해결하기 위해 기존에는 별도 함수를 선언하여 해결하는 식의 방법을 사용했지만 왈러스 연산자를 통하여 제대로 처리할 수 있게 됐다.

파이썬에서는 switch/case 문이나 do/while 루프를 쓸 수 없지만, 대입식을 사용해서 흉내낼 수 있다.

# switch/case 처럼 왈러스문을 사용하는 예
if (count := fresh_fruit.get('banana', 0)) >= 2:
    pieces = slice_bananas(count)    
		to_enjoy = make_smoothies(pieces)
elif (count := fresh_fruit.get('apple', 0)) >= 4:    
		to_enjoy = make_cider(count)
elif count := fresh_fruit.get('lemon', 0):
    to_enjoy = make_lemonade(count)
else:
    to_enjoy = 'Nothing'
bottles = []
while fresh_fruit := pick_fruit():
    for fruit, count in fresh_fruit.items():
        batch = make_juice(fruit, count)
        bottles.extend(batch)

16. in을 사용하고 딕셔너리 키가 없을 때 KeyError를 처리하기 보다는 get을 사용하라

✅ 딕셔너리의 키가 없을 때 처리는 get을 사용하는 게 좋다.

in, KeyError로 예외처리하는것은 안티패턴으로, get을 사용하는걸 권장한다. 딕셔너리에 내장된 get메소드 사용시 두번쨰 파라미터로 키가 들어가지 않았을 경우 반환할 디폴트 값을 설정할 수 있다.

counters = {
    '품퍼니켈': 2,
    '사워도우': 1,
}
key = '밀'

# 키가 존재하는지 in으로 확인(안티패턴)
if key in counters:
    count = counters[key]
else:
    count = 0
counters[key] = count + 10

# KeyError 예외처리(안티패턴)
try:
    count = counters[key]
except KeyError:
    count = 0
counters[key] = count + 1

# get
count = counters.get(key, 0)
counters[key] = count + 19*

✅  setdefault 메소드

setdefault 메소드는 키가 없는 경우 전달 받은 디폴트 값을 키에 연결시켜 딕셔너리에 대입하고 키에 연결된 값을 반환한다. 여기서 반환되는 값은 디폴트 값이 될수도 있고 이미 딕셔너리에 존재하던 값인지 알 수 없다. 하지만 저자의 의견으로 메소드의 이름자체가 메소드의 동작을 직접적으로 분명히 드러내지 못한다고 생각한다. 값을 얻는 메소드지만 set으로 시작되기에 오인의 여지가 있다고 한다.

또한 setdefault에 전달된 디폴트 값은 복사되지 않고 딕셔너리에 직접 대입된다. 이 이야기는 디폴트값을 setdefault에 전달할 때마다 그 값을 새로 선언해야할 수 있다는 뜻이다. 호출할때마다 리스트를 만들어야 하므로 성능이 크게 저하될 수 있다.

만약 setdefailt가 적합해 보인다면, defaultdict를 사용하는것을 고려해보자

  • 딕셔너리 키가 없는 경우를 처리하는 방법으로는 in 식을 이용하는 방법, KeyError 예외를 사용하는 방법, get 메서드를 사용하는 방법, setdefault 메서드를 사용하는 방법이 있다.
  • 카운터와 같이 기본적인 타입의 값이 들어가는 딕셔너리를 다룰 때는 get 메서드가 가정 좋고, 딕셔너리에 넣을 값을 만드는 비용이 비싸거나 만드는 과정에 예외가 발생할 수 있는 경우에도 get 메서드를 사용하는 편이 낫다.
  • 해결하려는 문제에 dict의 setdefault 메서드를 사용하는 방법이 가장 적합해 보인다면 setdefault 대신 defaultdict를 사용할지 고려해 보라.

'python' 카테고리의 다른 글

이펙티브 파이썬 2nd 정리 #5  (0) 2023.09.08
이펙티브 파이썬 2nd 정리 #4  (0) 2023.09.01
이펙티브 파이썬 2nd 정리 #3  (0) 2023.08.18
이펙티브 파이썬 2nd 정리 #2  (0) 2023.08.05