데이터를 걷는 선비

[Python] 함수 (1/2) 본문

Develop/Computer Science

[Python] 함수 (1/2)

세미제로 2023. 1. 8. 21:27

본 포스팅은 "파이썬 코딩의 기술(브렛 슬라킨 외 저)"를 보고 작성했습니다.

파이썬 주니어들이 시니어로 발돋움하기 위한 최고의 책이라고 생각되네요!!

 https://www.gilbut.co.kr/book/view?bookcode=BN002890 

 

Effective Python 2nd 이펙티브 파이썬 : 파이썬 코딩의 기술

똑똑하게 코딩하는 법, 개정판

www.gilbut.co.kr


파이썬 함수에는 프로그래머가 더 편하게 프로그래밍할 수 있도록 해주는 여러가지 추가 기능이 들어있다. 

이런 추가 기능을 사용하면 함수의 목적을 더 분명하게 표현할 수 있고, 코드의 잡음을 줄여서 함수 호출의 의도를 더 명확히 드러낼 수 있으며, 찾기 어려운 미묘한 버그를 현저히 줄일 수 있다.


BETTER WAY 1  "함수가 여러 값을 반환하는 경우 절대로 네 값 이상을 언패킹하지 마라"

  • 함수가 여러 값을 반환하기 위해 값들을 튜플에 넣어서 반환하고, 호출하는 쪽에서는 파이썬 언패킹 구문을 쓸 수 있다.
  • 그러나, 언패킹 구문에 변수가 네 개 이상 나오면 실수하기 쉽고, 가독성이 나빠진다.
  • 작은 클래스를 반환하거나 namedtuple 인스턴스를 반환하라!

BETTER WAY 2  "None을 반환하기보다는 예외를 발생시켜라"

  • 특별한 의미를 표시하는 None을 반환하는 함수를 사용하면 None이 아닌 값(0이나 빈 문자열)이 조건문에서 False로 평가될 수 있기 떄문에 실수하기 쉽다.
  • 특별한 상황을 표현하기 위해 None을 반환하는 대신 예외를 발생시켜라
def careful_divide(a, b):
    try:
        return a/b
    except ZeroDivisionError:
    	return None
        
x, y = 0, 5
result = careful_divide(x, y) #result는 0이고, 잘못된 입력이 아니지만 None과 0이 모두 False로 인식
if not result:
	print('잘못된 입력')
>>>
잘못된 입력

 

결코 None을 반환하지 않도록 수정

def careful_divide(a, b):
    try:
        return a/b
    except ZeroDivisionError as e:
    	raise ValueError('잘못된 입력')

BETTER WAY 3 "변수 영역과 클로저의 상호작용 방식을 이해하라"

  • 클로저: 클로저란 자신이 정의된 영역 밖의 변수를 참조하는 함수
  • 파이썬에서는 함수가 일급 시민(first class citizen) 객체: 일급 시민 객체라는 말은 이를 직접 가리킬 수 있고, 변수에 대입하거나 다른 함수에 인자로 전달할 수 있으며, 식이나 if문에서 함수를 비교하거나 함수에서 반환하는 것 등이 가능하다는 것을 의미

 

  • 식 안에서 변수를 참조할 때 파이썬 인터프리터는 다음 순서로 영역을 뒤진다.
    1. 현재 함수의 영역
    2. 현재 함수를 둘러싼 영역(현재 함수를 둘러싸고 있는 함수 등)
    3. 현재 코드가 들어 있는 모듈의 영역(전역 영역, global scope)
    4. 내장 영역(len, str 등의 함수가 들어 있는 영역)
#자신이 정의된 영역 밖의 변수를 참조하는 클로저 도우미 함수를 통해 정렬
def sort_priority(values, group):
    found = False
    def helper(x):
        if x in group:
            found = True
            return (0, x)
        return (1, x)
    values.sort(key=helper)
    
numbers = [8, 3, 1, 2, 5, 4, 7, 6]
group = {2, 3, 5, 7}
sort_priority(numbers, group)
print('발견:', found)
print(numbers)
>>>
발견: False
[2, 3, 5, 7, 1, 4, 6, 8]

 

따라서 found 변수는 helper 도우며 함수의 영역에서 True로 대입된다.

즉, helper 함수의 클로저 안에서 이 대입문은 helper 영역 안에 새로운 변수를 정의하는 것으로 취급되지, sort_priority 안에서 기존 변수에 값을 대입하는 것으로 취급되지는 않는다.


BETTER WAY 4 "변수 위치 인자를 사용해 시각적인 잡음을 줄여라"

  • 위치 기반 인자(positional argument)를 가변적으로 받을 수 있으면 함수 호출이 더 깔끔해지고 시각적 잡음도 줄어든다.
  • def 문에서 *args를 사용하면 함수가 가변 위치 기반 인자를 받을 수 있다.
def log(message, *values):
    #첫 번째 파라미터는 반드시 필요하지만, 그 이후의 모든 위치 인자는 선택 사항이다.
    if not values:
        print(message)
    else:
        values_str = ', '.join(str(x) for x in values)
        print(f'{message}: {values_str}')

log('내 숫자는', 1, 2)
log('안녕')
>>
내 숫자는: 1, 2
안녕

 

Comments