Effective Python - 함수 호출 - 1

Hyeseong·2022년 1월 6일
0

python

목록 보기
20/22

위치로만 인자를 지정하게 하거나 키어드만 인자를 지정하게 해서 함수 호출을 명확하게 하기

위치 함수를 사용한 일반적 사용 범례

키워드를 사용해 인자를 넘기는 기능은 파이선 함수의 강력한 기능중 하나에요.
키워드 인자를 활용하면 코드의 가독성이 매우 높아집니다. 다른말로는 코드의 품질이 높아져요.

예를 들어 한 숫자를 다른 숫자로 나눌 때 예외적으로 발생하는 오류를 처리 방법을 볼게요.

  • ZeroDivisionError

  • OverflowError

    위 두가지 오류처리에서 ZeroDivisionError 예외를 무시하고 무한대를 반호나하고 싶고 어떠한 경우에는 OverflowError 오류를 무시하고 0을 반환하고자 하는 경우가 있어요.

    def safe_division(number, divisor, ignore_overflow, ignore_zero_division):
    	try:
       	return number / divisor
       except OverflowError:
       	if ignore_overflow:
           	return 0
           else:
           	raise
       except ZeroDivisionError:
       	if ignore_zero_division:
           	return float('inf')
           else:
           	raise

    아래는 위 정의한 함수를 호출하는 예시에요.

  result = safe_division(1.0, 10**500, True, False)
  print(result)
  >>>
  0
result = save_divisioin(1,0, 0, False, True)
print(result)

>>>
inf

키워드 인자를 사용한 가독성 향상

문제는 어떤 예외를 무시할지 결정하는 두 bool 변수의 위치를 혼동하기 쉽다는 것이조. 그로인해 bug가 발생할수도 있고요.
방법은 코드 가독성을 높이는 것이며 그중 키워드 인자를 사용하는 것이에요

def save_division(number, 
				  divisior, 
                  ignore_overflow=False, 
                  ignore_zero_division=False): 
    ...
    

함수 호출시 키워드 인자를 사용해 무시할 예외를 설정하는 flag를 이용할 수 있어요.

result = save_divisioin(1,0, 10**500, ignore_over_flow=True)
print(result)

result = save_divisioin(1,0, 0, ignore_zero_division=True)
print(result)

>>> 
0
inf

키워드 인자를 사용 강제 할 수 없는 영역?!

위와 같은 방식으로 키워드 인자를 사용하는 것이 선택적인 사항이므로 호출 하는 쪽에서 명확성을 위해 키워드 인자를 꼭 쓰도록 강요할 수 없다는데 있습니다. 새로 정의한 safe_division_b에서도 여전히 위치 인자를 통해 예전 방식으로 함수를 호출 할 수 있습니다.


assert safe_division_b(1.0, 10**500, True, False) == 0

이와 같이 복잡한 함수의 경우 호출자가 키워드만 사용하는 인자를 통해 의도를 명확히 밝히도록 요구하는 편이 좋다. 키워드만 사용하는 인자는 키워드를 반드시 사용해 지정해야 하며, 절대 위치를 기반으로는 지정할 수 없다.

* 기호를 사용하여 위치인자와 키워드 인자를 구분 시키기

def save_division_c(number, divisior, *, 
					ignore_overflow=False,
                    ignore_zero_division=False):
     ...
     

이제 이 함수를 호출하면서 키워드 인자를 써야 하는데, 위치 인자를 사용하면 프로그램이 제대로 작동하지 않는다.

safe_division_c(1.0, 10**500, True, False)

>>>
Traceback...
TypeError: safe_division_c() take 2 positional arguments but 4 were given

하지만 키워드 인자와 디폴트 값은 예상대로(아래 예에서 하나의 경우는 예외를 무시하고, 다른 경우는 예외를 던진다) 잘 작동함

result = safe_division_c(1.0, 0, ignore_zero_division=True)
assert result == float('inf')

try:
	result = safe_division_c(1.0, 0)
except ZerodivisionError:
	pass #예상대로 작동함

위치와 키워드를 혼용 할 수 있는 문제점

하지만 이 safe_division_c 함수에도 문제가 있어요. 호출하는 쪽에서 이 함수의 맨 앞에 있는 두 필수 인자(number=2, divisor=5)를 호출하면서 위치와 키워드를 혼용 할 수 있다.

assert safe_division_c(number=2, divisor=5) == 0.4
assert safe_division_c(divisor=5, number=2, ) == 0.4
assert safe_division_c(2, divisor=5) == 0.4

추후 요구 사항이 바뀌거나 원하는 스타일이 바뀌어서 맨 앞의 두 인자 이름이 변경 될 수도 있어요.

def save_division_c(numerator, denominator, *, 
					ignore_overflow=False,
                    ignore_zero_division=False):
     ...
     

이런 사소한 변경만으로도 number와 divisor 인자를 키워드로 호출하는 기존 호출 코드는 쉽게 깨져 버리게 되요.

safe_division_c(number=2, divisor=5)

>>>
Traceback...
TypeError: safe_division_c() got an unexpected keyword argument
'number'

이 상황에서는 또 다른 문제가 야기 되는데, 함수의 명시적 인터페이스에 number와 divisor를 포함시키는 것을 의도하지 않았기 때문에 일어 나네요.

파이썬 3.8에서는 이 문제를 해결할 방법을 제시하여 줍니다.

위치로만 지정하는 인자

말 그대로 위초로만 지정하는 인자는 반드시 위치만 사용해 인자를 지정해야하고 키워드 인자로는 쓸 수 없어요.

def save_division_c(number, divisior, ,/ , *, 
					ignore_overflow=False,
                    ignore_zero_division=False):
     ...

필수 인자를 위치인자로 지정할 제대로 작동하는지 아래코드를 통해 확인해볼게요.

assert safe_division_d(2, 5) == 0.4

하지만 필수 인자를 위치로만 지정하는 인자를 키워드를 사용해 지정하면 예외가 발생해요.

assert safe_division_d(numerator=2, denominator=5) == 0.4

>>>
Traceback...
TypeError: safe_division_c() got some positional-only arguments
passed as keyword arguments: 'numerator, denominator'

이제 safe_division_d 함수 정의에서 맨 앞의 두 필수 인자는 호출하는 쪽과 분리 되었어요. 따라서 파라미터 이름을 바꾸어도 망가지지 않아요.

positional-only, keyword-only 인자 사용 추가 장점

인자목록에서 / 와 * 기호사이에 있는 모든 파라미터는

  • 위치를 사용해 전달 가능
  • 이름을 키워드로 사용해 전달 가능

결국 구현하고자 하는 API의 스타일이나 필요에 따라 두 인자 전달 방식을 모두 사용하면 가독성을 높이고 잡음도 줄일수 있어요.

예를 들어 다음 코드에서는 결과를 표시 할 때 얼마나 많은 자릿수를 사용할 지 결정하고자 반올림할 위치를 지정하는 인자를 safe_division에 추가한다고 해볼게요.

 def safe_division(numerator, denominator, /,
 				   ndigits=10, *, 
                   ignore_overflow=False, 
                   ignore_zero_division=False):
 	try:
    	fraction = numerator / denominator 
    	return round(fraction, ndigits)
    except OverflowError:
    	if ignore_overflow:
        	return 0
        else:
        	raise
    except ZeroDivisionError:
    	if ignore_zero_division:
        	return float('inf')
        else:
        	raise

위 정의된 함수에서 ndigits는 위치나 키워드를 사용해 전달할 수 있는 선택적인 파라미터이므로 어떤 방식으로든 함수 호출에 사용할 수 있어요.

result = safe_division_e(22, 7)
print(result)

result = safe_division_e(22, 7, 5)
print(result)

result = safe_division_e(22, 7, ndigits=2)
print(result)

>>>
3.1428571429
3.14286
3.14

요약

  • keyword-only 인자를 사용하면 호출하는 쪽에서 특정 인자를(위치를 사용하지 않고) 반드시 키워드를 사용해 호출하도록 강제할 수 있다. 이로 인해 함수 호출의 의도를 명확히 할 수 있다. keyword-only는 인자 목록에서 * 다음에 위치한다.

  • positional-only 인자를 사용하면 호출하는 쪽에서 키워드를 사용해 인자를 지정하지 못하게 만들 수 있고 이에 따라 함수 구현과 함수 호출 지점 사이의 결합을 줄일 수 있다. positional-only는 함수 선언부의 인자 나열 목록에서 / 기호 앞에 위치 시킨다.

  • 인자 나열 목록에서 /와 * 사이에 있는 파라미터는 키워드를 사용해 전달해도 되고 위치를 기반으로 전달해도 된다. 이런 동작은 파이썬 함수 파라미터의 기본 동작이다.

profile
어제보다 오늘 그리고 오늘 보다 내일...

0개의 댓글