한글판 johnson 2008.05.10 토

파이썬 여행 #1: 변경가능 객체를 기본 인자로 건네기

March 10, 2007 - 8:46am — frank

기본 함수 인자를 허용하는 현대 언어가 많다. 기본 인자는 멋진 것이 될 수 있다 - 덕분에 공통 사례에 대하여 지혜롭게 기본값을 사용할 수 있다. 필요한 대로 기능을 더 추가할 수 있는 힘을 빼앗기지 않고서 말이다. 새로운 매개변수에 대하여 합리적인 기본값을 제공하면 기존의 코드를 깨지않고 함수 정의를 마음대로 확대할 수 있다.

다음은 (리스트나 사전 또는 객체 실체처럼) 변경가능 객체를 기본 인자로 사용하면 파이썬이 어떻게 여러분을 물어뜯는지 보여주는 재미있는 예이다. 다음은 내가 작성하고 싶은 예제 함수이다 ...

# OK, now I am *INTENDING* to write a routine to do the following:
#
#    Append an item to a list, returning the list.
#    If no list is passed, a new list is created.
#
# Here is my first attempt at implementation ...
def add_item(item, the_list = []):    
    the_list.append(item)
    return the_list


처음 보면 의도한 대로 일을 해주는 것 같이 보인다. 사용자가 원소를 추가하기 위해 리스트를 건네지 않으면, 새로운 리스트가 시작된다. 또는 적어도 그렇게 될 것 같아 보인다. 실행해 보자:

# I want to start a new list ...
word_list = add_item('First')

# Add some more words
word_list = add_item('Second', word_list)
word_list = add_item('Third', word_list)

# Start a new list of numbers ...
number_list = add_item(111)

# Add some more numbers
number_list = add_item(222, number_list)
number_list = add_item(333, number_list)

# Now lets see what happened ...
print "word_list ",word_list
print "number_list ",number_list


이를 실행하면, 다음과 같은 출력을 얻는다:

word_list  ['First', 'Second', 'Third', 111, 222, 333]
number_list  ['First', 'Second', 'Third', 111, 222, 333]


와우! 예상대로가 아니었다. 변경가능 객체를 기본 인자로 사용하면, 파이썬은 객체 하나를 만들고, 호출할 때마다 같은 객체를 재사용한다. 개인적으로 이것이 직관적으로 보이지 않지만, 그것이 파이썬이 작동하는 방식이다. 람다 함수에 인자를 건네는 것과 무엇인가 관련이 있다고 생각한다. 그러나 내가 완전히 틀릴 수도 있다.

어쨌든, 다음은 내가 원하는 결과를 달성하도록 함수를 코딩하는 올바른 방법이다:

# fixed version ...
def add_item(item, the_list = None): # declare default as None, then ...

    # NOW, I can set to a new list if no list is passed
    the_list = the_list or []  
    the_list.append(item)
    return the_list


이제 프로그램을 실행하면 예상대로 결과를 얻는다:

word_list  ['First', 'Second', 'Third']
number_list  [111, 222, 333]


변경가능 유형을 기본 인자로 사용한다고 해서 모든 실체가 다 문제를 일으키는 것은 아님에 주의하자. 사실, 아마도 대부분의 경우에는 문제가 없을 것이다. 위험은 문제를 예상하지 못할 때이며, 왜 코드가 작동하는지 이해하지 못할 때이다. 재귀 함수에서 문제가 더 많이 일어나는 것 같다.

어쨌든, 개인적으로 본인은 변경가능 유형을 절대로 기본인자로 사용하지 않는 것을 원칙으로 하며, 대신 위에 보여준 바와 같이 언제나 None을 기본인자로 사용한다. 그 다음 그 변수를 함수 몸체 안에 설정한다 (거기에서는 언제든지 새로운 빈 객체가 만들어진다).

간단하고 깔끔하며 한 줄이 들뿐이다. 무엇이 잘못되었는지 알아내기 위해 드는 기나긴 디버깅 시간을 절약할 수 있다 (고통스런 경험을 통해 말씀 드리는 바임!)


Written in WikklyText.