이번 3장에서는 새로운 기술을 알려준다기보단 어떻게 함수를 짤 때 좋은 코드를 작성할 수 있는지에 대한 팁이 많이 녹아져 있었다. 따라서 이번 편에서는 1. 함수 작성 팁과 2. 데코레이터에 대해 기술하겠다. 키워드 인자, 디폴트 인자 설정에 대해서는 정리하지 않을 예정이다.
1. 함수 작성 팁
함수 return값에 4개 값 이상을 언패킹 하지 말기
너무 많은 값을 한꺼번에 언패킹하면 가독성이 떨어지고, 나중에 함수를 바꾸거나 재사용할 때 순서를 혼동하는 등 어려움이 발생한다.
#ex : 많은 값을 언패킹하지 말자!
def my_function(a, b):
return a+b, a/b, a*b, a*2, a**2, b**2
add_result, * = my_function(a = 2, b = 3)
return None 대신에 에러를 발생시키기
개인적으로 나도 코드를 작성할 때 특정한 경우들에 return None을 즐겨 쓰는 경향이 있다. None을 쓰는 것을 지양해야 하는 이유는 아래 예시를 보면 확실히 알 수 있다.
#Division 예시
def divide_fcn(a, b):
try:
return a/ b
except ZeroDivisionError:
return None
tmp1 = divide_fcn(0, 1) # return 0
tmp2 = divide_fcn(1, 0) # return None
#None인지 검사하는 코드
if not tmp1:
print('error!')
if not tmp2:
print('error!')
#### 두 if문 다 error를 print한다
나는 보통 그래서 return값에 boolean을 추가하는 편이긴 하지만, 아래와 같이 Error를 발생시키는 방법을 연습하면 좋을 듯하다.
#ex
def division_fcn2(a, b):
try:
return a/b
except ZeroDivisionError as e:
raise ValueError('error!')
클로저(closure) 사용하기
- 클로저는 자신이 정의된 영역 밖의 변수를 참조하는 함수이다.
- 함수는 파이썬에서 "일급 시민(first class citizen) 객체"이다. 즉 변수에 대입하거나, 다른 함수에 인자로 전달되거나, if와 같은 식에서 함수를 비교하거나 하는 등이 가능하다.
- 파이썬은 시퀀스를 비교하는 구체적인 규칙이 있다. 예를 들어 튜플 내 원소를 비교할 때 0번째 원소가 같으면 그다음인 1번째 원소를 비교하는 것이다. 이를 통해 클로저로 정렬할 수 있다.
def sort_fcn(values, group):
def helper(x): #클로저 기능 사용! 이 helper함수가 자신의 영역 밖에 있는 group을 참조할 수 있음
if x in group:
return (0, x)
else:
return (1, x)
values.sort(key = helper)
sort_fcn(values, group)
(매우 중요!) 여기서 짚고 넘어가야 하는 것은 파이썬 인터프리터가 변수를 참조할 때의 순서다.
- 현재 함수의 영역
- 현재 함수를 둘러싼 영역 (ex. 위 예시로 보자면 helper의 기준에서 sort_fcn이 될 수 있음)
- 현재 코드가 들어 있는 모듈의 영역. == 전역 영역
- 내장 영역 (ex. len, str 등의 내장 함수가 있는 영역)
변수에 값을 대입할 때는 아래와 같은 방식으로 작동한다.
변수가 현재 영역에 있을 때 -> 변숫값을 새로운 값으로 바꿔줌
변수가 현재 영역에 없을 때 -> 새로운 변수 정의로 취급(1번과 같은 방식이 되게 하고 싶다면 nonlocal로 변수 선언을 해당 함수 내에 해줘야 함. 되도록 하지 말자.)
def sort_fcn(values, group):
TF = True
def helper(x):
TF = False
if x in group:
return (0, x)
else:
return (1, x)
values.sort(key = helper)
return TF
tmp = sort_fcn(values, group) # tmp = True
곰곰히 생각해보면 위와 같이 작동해야 함수가 안전하게 동작한다.
변수 위치 인자 사용하기
*args를 사용해 가변적으로 인자를 받아준다. 함수로 들어가기 전에 튜플로 변환되는 특성이 있다.
#사용 예시
def my_function(a, *args):
...
a=0
my_list=[1,2,3]
my_function(a, my_list)
가변 인자로 넣을 때 generator는 넣지 않도록 주의해야 한다. 튜플로 변환되기 때문에 메모리 에러가 날 수 있다.
변수 위치 인자는 들어가는 인자들이 변경될 때 코드가 잘 작동하지 않을 수 있으므로 주의하자.
딕셔너리 형태로 값을 줄 수 있다. **kwargs를 사용하면 된다. (cf. 노파심에 쓰는 말. 무조건 변수명이 args, kwargs일 필요 없다!!)
def my_function(**kwargs):
return a + b
my_dic = {'a':1 , 'b':2, 'c':'hello'}
my_function(my_dic) #return 3
(그래도 가독성을 생각한다면) 키워드 인자를 사용하기
2. 함수 데코레이터
데코레이터는 자신이 감싸고 있는 함수가 호출되기 전, 후에 코드를 추가로 실행하는데, 보통 디버깅, 함수 등록 등에 사용한다. 아래 코드와 같이 데코레이터를 정의할 수 있다.
def trace(func): #func는 데코레이터가 받을 함수
def wrapper(*args, **kwargs): #wrapper를 선언해주자
result = func(*args, **kwargs)
print(f'{func.__name__}({args!r}, {kwargs!r})' f'->{result!r}')
#함수 func 진행 후에 print가 실행되고 있다.
#이해하기 편하게 책 본문 print문을 그대로 썼다.
#참고 : f-문자열 내 {!r}은 repr()을 의미한다.
return result
return wrapper
이제 데코레이터를 적용해보면 다음과 같이 함수를 작성할 수 있다. 참고로 @은 데코레이터 호출 후 반환 결과를, 원래 함수가 속하는 영역에 원래 함수 이름으로 등록하는 것과 같다. 즉, trace 내 wrapper함수가 my_function이라는 이름으로 등록된다. 예시에서는 데코레이터가 함수 실행 후를 장식하고 있다.
#@ 뜻 : 데코레이터를 사용하겠다!
@trace
def my_function(a,b):
'''간단한 덧셈함수를 써볼까?'''
print(a+b)
return a+b
my_function(a=1, b=2)
# 3
# my_function((1,2),{})->3
이때 my_function에 내장 함수 help를 호출한다면 어떻게 될까? 원래대로라면 '''간단한 덧셈 함수를 써볼까?'''가 반환돼야 할 것이다. 하지만 아래 이미지와 같이 데코레이터가 감싸고 있는 원래 함수 위치를 찾을 수 없어 독스트링이 정상적으로 반환되지 않는다.
그렇기 때문에 내장 데코레이터인 wraps를 추가해준다.
from functools import wraps
def trace(func): #func는 데코레이터가 받을 함수
@wraps(func)
def wrapper(*args, **kwargs): #wrapper를 선언해주자
result = func(*args, **kwargs)
print(f'{func.__name__}({args!r}, {kwargs!r})' f'->{result!r}')
#이해하기 편하게 책 본문 print문을 그대로 썼다.
#참고 : f-문자열 내 {!r}은 repr()을 의미한다.
return result
return wrapper
wraps는 데코레이터 내부에 들어가는 함수 내 중요 메타데이터를 복사해서 데코레이터가 적용된 함수에 적용해준다. 이제 help을 호출하니 독스트링이 잘 반환된다.
코드를 작성할 때 데코레이터에 익숙하지 않았는데, 책을 읽으면서 적용해봐야겠다 싶은 것들이 늘어서 좋다.
'python > 파이썬 코딩의 기술' 카테고리의 다른 글
파이썬 코딩의 기술 리뷰 - 클래스, 인터페이스 (0) | 2022.01.16 |
---|---|
파이썬 코딩의 기술 리뷰- 동시성과 병렬성 1 (2) | 2022.01.10 |
파이썬 코딩의 기술 - 컴프리헨션, 제너레이터 (0) | 2022.01.09 |
파이썬 코딩의 기술 - 리스트, 딕셔너리 (0) | 2021.12.19 |
파이썬 코딩의 기술 리뷰 - self와 cls, bytes와 str, f-문자열, 왈러스 연산자 (2) | 2021.12.12 |