본문 바로가기

Python_programming/초중급편

파이썬 함수 코드 스타일 PEP20 - 1

구글링하면서 보게 되는 파이썬으로 된 오픈소스들을 보면서  왜 항상 이러한 구문들이 들어가 있을까? 

라고 생각되는 코드들이 있습니다. 

 

최근 보고 있는 책('파이썬을 여행하는 히치하이커를 위한 안내서')에서 이에 대한 실마리를 알 수 있었고,

이번 포스팅부터는 그에 대해 조금씩 정리해보고자 합니다. 

 

우선, 대부분의 파이썬 코드들은 PEP20 을 따르는 것으로 보인다. 

최근 나도 직접 함수나 클래스를 짜게 될 상황에 닥쳐 있는데, 이러한 가이드들이 도움이 된다. 

 

1) Errors should never pass silently. (오류 앞에서 절대 침묵하지 말지어다. ) 

 

아래 코드는 절대 실행하지 말자.

 

while True:
	try:
    	print("nyah",end =" ")
    except:
    	pass

 

except 구문에 예외 종류를 명시하지 않으면 KeyboardInterrupt 를 포함한 모든 오류를 무시(pass)한다. 

처리하고자 하는 예외만 except 문에 명시하자.

 

while True:
	try:
    	print("ni",end="-")
    except:
    	print("An exception happened. Raising.")
        raise

 

위에서처럼 예외가 발생했음을 알린 뒤, 예외를 강제로 다시 발생(re-Raise)시킨다면 상관없다. 오류는 꼭 예외처리하거나 재발생 처리하여 그대로 묻히는 일이 없도록 하자. 

 

2) 함수 인자는 사용하기에 직관적이어야 한다,

 

def func(positional, keyword=value, *args, **kwargs):
	pass

 

위치인자는 필수이며 기본 값을 가지지 않는다.

키워드 인자는 선택 사항이며 기본 값을 가진다.

가변 인자 리스트는 선택사항이며 기본 값을 가지지 않는다.

가변 키워드 인자 딕셔너리는 선택 사항이며 기본 값을 가지지 않는다. 

 

위치 인자: 함수의 인자 개수가 많지 않고, 그들의 순서가 자연스레 함수의 의미를 설명할 때 사용하자. 예를 들면, send(message, recipient) 나 point(x,y) 는 인자가 2개이며 그 순서를 외우기 쉽다. 함수를 호출 할 때 위치 인자의 이름을 사용하거나 위치 인자의 순서를 바꾸지 말자. point(y=2, x=1) 은 가독성이 낮고 불필요하게 장황하다. 

 

키워드 인자: 함수가 2~3개 이상의 위치 인자를 가진다면 함수의 시그니처를 기억하기 어려워지므로, 기본 값을 가지는 키워드 인자를 추가적으로 사용하는 것이 좋다. send(message, to, cc=None, bcc=None) 와 같은 함수 시그니처를 갖게 된다. 여기서 cc와 bcc는 선택 입력 가능한 키워드 인자이며, 입력되지 않는 경우 None 으로 처리된다. 

 

가변 인자 리스트: 함수를 설계할 때 위치 인자의 개수를 미리 정해 두기 애매하다면 *args 로 정의되는 가변 인자 리스트를 사용해보자. 사용자가 원하는 개수의 위치 인자를 함수에 전달하면 이들은 함수 바디 내에서 args 라는 튜플로 존재하게 된다. 

 

ex) send(message, *args) 는 미리 정해지지 않은 인원의 수신인 명단을 인자로 받기 위해 *args 를 사용하였다.  send("42","Frankie","Benjy","Trillian")과 같이 함수를 호출하면 함수 바디로 args=("Frankie", "Benjy", "Trillian") 이라는 튜플이 전달된다. 

 

가변 키워드 인자 딕셔너리  **kwargs

: 로그를 남길 때 기본 메시지 외에 추가 정보를 함께 남기는 데 유용하다. 사용자가 미리 포매터에 지정해 준 대로 로그 종류에 관계 없이 필요한 정보가 추출되고 기록된다. 

 

3) 함수의 결과 값은 한 곳에서만 반환하자 

 

함수 실행이 종료되는 경우는 2가지다. 하나는 오류가 발생하여 종료되는 경우이고, 다른 하나는 정상적으로 실행되어 결과 값을 반환한 뒤 종료되는 경우이다. 오류 발생시에는 None 또는 False 반환이 적절하다. 이 때, 함수에서 오류와 관련된 문맥이 파악되자마자 결과 값을 반환하도록 하여 함수 구조를 수평적으로 만드는 것이 좋다. 만약 오류가 발생하지 않는다면, 그 다음 코드가 실행되기 위한 조건이 충족된 것이며, 함수 실행이 멈추지 않고 지속된다. 가끔은 여러 개의 반환문을 사용하는 것도 필요하다. 

 

하지만 가능한 하나의 종료 지점만을 설정하자. 종료 지점이 여러 개라면 함수의 최종 결과 값이 어디서 반환되었는지 파악하기 힘들어져 함수를 디버깅하기 어렵다. 이에 반해 단 하나의 종료 지점을 가지도록 하면 코드 진행 경로를 파악하기  쉬워진다. 여러 개의 종료 지점은 코드 리팩터링(refactoring)에 대한 힌트가 되기도 한다. 

 

 

def select_ad(third_party_ads, user_preferences):
	if not third_party_ads:
    	return None # 예외 발생이 더 나음
    if not user_preferences:
    	return None # 예외 발생이 더 나음
    # 주어진 ad 와 개별 설정 중 best_ad 를 선택하는 복잡한 코드
    # 여기서 bset_ad 가 결정되도 바로 반환하지 말자
    if not best_ad:
    
   	return best_ad # 하나의 종료 지점에서 결과값을 반환하면 코드 유지 관리가 편함. 

4) 같음을 확인하기 위한 대안 

 

주어진 값을 True, None, 0 과 명시적으로 비교할 필요가 없다면 다음 예시를 참고하여 if 문에 그 값만 써도 좋다. 

 

attr 이라는 값이 존재해서 print 되었고

값이 존재하지 않을 경우라는 if not attr 라는 조건은 성립되지 않아서 false 되지 않았습니다.

그렇다면, attr 값이 없다면 실행될까요? 

 

주피터 노트북을 새로 열어서 지역 변수 목록을 확인해보았습니다. 아무 변수도 없습니다. 

근데 attr 이 없는데 if not attr 이라는 조건에서 not defined 에러가 났습니다...

 

attr 을 None 으로 선언하면 if not attr 조건이 성립됩니다. 1일 때는 성립되지 않습니다. 

0일 때는 if not attr 조건이 성립합니다.  

뭔가 좀 헷갈리기 시작합니다. 

 

0, false, None 일 때는 값이 존재하지 않는다라고 인식합니다. 

그렇다면 애초에 선언을 하지 않아야 값이 존재하지 않는 거 아닐까요? 

제 생각에는 그 판별이 attr 의 타입에 있는 것 같습니다. 

int타입일때는(숫자 0), bool 타입일 때 (false),  None 타입일 때 값이 존재하지 않는 것(false)으로 인식하는 걸로 보입니다. 

 

그러니 애초에 변수 자체가 선언되지 않았으면 if 에서는 true, false 여부를 판단하지 않는 것으로 보입니다. Nonetype 의 경우에는 metaclass 개념들과 관련이 있는데 이는 나중에 다뤄 보겠습니다. 

 

+) true value testing

 

https://www.tutorialspoint.com/python-truth-value-testing

 

Python Truth Value Testing

Python Truth Value Testing We can use any object to test the truth value. By providing the condition in the if or while statement, the checking can be done. Until a class method __bool__() returns False or __len__() method returns 0, we can consider the tr

www.tutorialspoint.com

도대체 값이 존재하지 않는다는게 파이썬에서는 어떤 형식으로 판단할까요? 위에 나온 튜토리얼을 따라 해보겠습니다. 

일단, 우리가 생각하는 값이 존재하지는 '무' 같은 개념이랑은 조금 다른 것 같습니다. 

 

우선 클래스를 하나 만들고 인스턴스화 했을 때 이는 true 로 판단합니다. 값이 존재하는 것이죠. 

 

근데 클래스 B 의 경우에는 인스턴스화 했으니 존재하는 건데도 조건문에서 존재하지 않는다. 즉, False 로 인식되는 걸로 보입니다. 왜냐면 스폐셜 메서드 bool 에서 false 가 리턴되기 때문이죠. 아하. 그러면 기본적으로 객체는 true 인건데 false, 0, None 으로 return을 해버리면 파이썬에서는 객체가 존재하지 않는? 걸로 판단하는 걸로 보입니다. 

 

 

컬렉션 타입의 객체에서는 형태가 있고 안이 비어 있으면 false 로 분류합니다. (=존재하지 않는다) 

 

false의 결론입니다. 우리가 흔히 생각하는 존재하지 않는다? 와는 조금 다른 느낌이 듭니다. 파이썬이라는 모든 게 인스턴스화 되는 언어의 세상에서 존재하지 않는다는 무언가 존재하는데 false 이거나 none 인거죠. 컬렉션 타입의 객체에서는 형태만 있고 안이 비어있는 것이구요. 그러니, 애초에 변수를 선언하지 않은 건 파이썬 세상에서는 존재한다 안한다로 얘기할 주제 자체가 아닙니다. 무언가 있어야 존재하는지 안하는지 보는거죠. 

 

지금 제가 하는 말이 무슨 x 소리냐 라고 생각할 수도 있지만 저도 곰곰히 생각하면 말이 좀 이상한 것 같네요. 우리 세상의 존재 유무 관점에서 보면 말이죠. 제가 영어를 잘못해서 제가 이해한 게 좀 엉뚱하거나 다른 방향일수도 있습니다. 여튼, 파이썬 세상에서의 존재하지 않는다라는건 false, none, 0 으로 return 되었다는 거죠. 객체가 말입니다. 이 부분은 아무래도 파이썬의 철학과 관련이 있는 것 같습니다. 

 

5) 변경 가능한 기본 인자

 

my_other_list 에서 [42] 만 나올 줄 알았는데 그전에 호출했던 12가 들어있다. 파이썬의 기본 인자는 함수가 호출될 때마다 만들어지지 않고, 함수가 정의될 때만 한 번 만들어진다. 변경 가능한 기본 인자를 사용하면, 매 호출마다 같은 객체를 사용하게 된다. 

 

 

함수를 호출할 때마다 새 객체가 만들어지도록, None을 선택하자.

 

 

 

이상 포스팅을 마치겠습니다 :)