빈 목록 인 기본 매개 변수를 피하는 파이썬적인 방법은 무엇입니까?


117

때로는 빈 목록 인 기본 매개 변수를 갖는 것이 자연스러워 보입니다. 그러나 파이썬은 이러한 상황에서 예기치 않은 동작을 제공합니다 .

예를 들어, 기능이 있습니다.

def my_func(working_list = []):
    working_list.append("a")
    print(working_list)

처음 호출하면 기본값이 작동하지만 그 이후의 호출은 기존 목록을 업데이트하고 (각 호출마다 하나의 "a"포함) 업데이트 된 버전을 인쇄합니다.

그래서, 내가 원하는 행동을 얻는 비단 법적인 방법은 무엇입니까 (각 호출에 대한 새로운 목록)?


17
이런 일이 발생하는 이유에 관심있는 사람들의는 체크 아웃 경우 effbot.org/zone/default-values.htm
라이언 이닝을

버그로 표시하려면 약간 더 복잡한 예제가 필요하지만 세트에 대해서도 동일한 동작이 발생합니다.
abeboparebop

링크가 죽으면 이것이 바람직한 동작임을 명시 적으로 지적하겠습니다. 기본 변수는 함수 정의 (처음 호출 될 때 발생)에서 평가되고 함수가 호출 될 때마다 평가되지 않습니다. 따라서 변경 가능한 기본 인수를 변경하면 후속 함수 호출은 변경된 객체 만 사용할 수 있습니다.
Moritz

답변:


150
def my_func(working_list=None):
    if working_list is None: 
        working_list = []

    working_list.append("a")
    print(working_list)

문서None 에서는 기본값으로 사용 하고 함수 본문에서 명시 적으로 테스트 해야한다고 말합니다 .


1
다음과 같이 말하는 것이 더 낫습니까 : if working_list == None : 또는 if working_list : ??
John Mulder

2
이것은 추악하기 때문에 마음에 들지 않더라도 파이썬에서 선호하는 방법입니다. 가장 좋은 방법은 "working_list가 None 인 경우"입니다.
e-satis

21
이 예제에서 선호하는 방법은 다음과 같이 말하는 것입니다. if working_list가 None입니다. 호출자는 사용자 지정 추가와 함께 빈 목록과 같은 개체를 사용했을 수 있습니다.
tzot

5
Mohit Ranka : not working_list가 길이가 0 인 경우 True라는 점에 유의하세요. 이로 인해 일관성이없는 동작이 발생합니다. 함수가 어떤 요소가 포함 된 목록을 받으면 호출자는 목록을 업데이트하고 목록이 비어 있으면 목록을 업데이트합니다. 건드리지 않을 것입니다.
vincent

1
@PatrickT 올바른 도구는 경우에 따라 다릅니다. varargs 함수는 (선택 사항) 목록 인수를 사용하는 함수와 매우 다릅니다. 둘 중 하나를 선택해야하는 상황은 생각보다 자주 발생하지 않습니다. Varargs는 인수 개수가 변경되면 훌륭하지만 코드가 WRITTEN이면 수정됩니다. 당신의 예처럼. 런타임 변수이거나 f()목록에서 전화를 걸려면 f(*l)총체적인 전화 를 해야합니다 . 더 나쁜 것은 구현 mate(['larch', 'finch', 'robin'], ['bumble', 'honey', 'queen'])이 varargs로 인해 SUCK라는 것입니다. 그건 훨씬 더 나은 경우 def mate(birds=[], bees=[]):.
FeRD

26

기존 답변은 이미 요청한 직접적인 솔루션을 제공했습니다. 그러나 이것은 새로운 Python 프로그래머에게 매우 일반적인 함정이므로 Python이 이러한 방식으로 작동하는 이유에 대한 설명을 추가 할 가치가 있습니다. 이는 " The Hitchhikers Guide to Python "에 " Mutable Default Arguments " 로 잘 요약되어 있습니다 . http : // docs .python-guide.org / en / latest / writing / gotchas /

인용구 : " Python의 기본 인수는 함수가 호출 될 때마다가 아니라 함수가 정의 될 때 한 번 평가됩니다 (예 : Ruby). 즉, 변경 가능한 기본 인수를 사용하고 변경하면 함수에 대한 모든 향후 호출에 대해서도 해당 개체를 변경했습니다. "

이를 구현하기위한 샘플 코드 :

def foo(element, to=None):
    if to is None:
        to = []
    to.append(element)
    return to

13

이 경우 중요하지는 않지만 객체 ID를 사용하여 None을 테스트 할 수 있습니다.

if working_list is None: working_list = []

부울 연산자 또는 파이썬에서 정의되는 방법을 활용할 수도 있습니다.

working_list = working_list or []

호출자가 working_list로 빈 목록 (거짓으로 계산 됨)을 제공하고 함수가 그가 제공 한 목록을 수정하기를 기대하는 경우 예기치 않게 작동하지만.


10

함수의 의도가로 전달 된 매개 변수 를 수정 하는 working_list것이라면 HenryR의 답변을 참조하십시오 (= None, 내부 없음 확인).

그러나 인수를 변경하지 않으려는 경우 목록의 시작점으로 사용하고 간단히 복사 할 수 있습니다.

def myFunc(starting_list = []):
    starting_list = list(starting_list)
    starting_list.append("a")
    print starting_list

(또는이 간단한 경우에는 그냥 print starting_list + ["a"]장난감 예제 일 뿐이라고 생각합니다)

일반적으로 인수를 변경하는 것은 Python에서 나쁜 스타일입니다. 객체를 완전히 변형시킬 것으로 예상되는 유일한 함수는 객체의 메서드입니다. 선택적 인수를 변경하는 것은 더욱 드뭅니다. 일부 호출에서만 발생하는 부작용이 정말 최상의 인터페이스입니까?

  • "출력 인수"라는 C 습관에서 수행하면 완전히 불필요합니다. 항상 여러 값을 튜플로 반환 할 수 있습니다.

  • 중간 목록을 작성하지 않고 긴 결과 목록을 효율적으로 작성하기 위해 이렇게하면 생성기로 작성하고 result_list.extend(myFunc())호출 할 때 사용 하는 것이 좋습니다. 이렇게하면 호출 규칙이 매우 깨끗하게 유지됩니다.

옵션 인수가 돌연변이 한 패턴 입니다 자주 수행은 재귀 함수의 숨겨진 "메모"인수입니다 :

def depth_first_walk_graph(graph, node, _visited=None):
    if _visited is None:
        _visited = set()  # create memo once in top-level call

    if node in _visited:
        return
    _visited.add(node)
    for neighbour in graph[node]:
        depth_first_walk_graph(graph, neighbour, _visited)

3

주제에서 벗어 났을 수도 있지만 가변 개수의 인수 만 전달하려는 경우 비단뱀적인 방법은 튜플 *args또는 사전 을 전달하는 것 **kargs입니다. 이는 선택 사항이며 구문보다 낫습니다 myFunc([1, 2, 3]).

튜플을 전달하려면 :

def myFunc(arg1, *args):
  print args
  w = []
  w += args
  print w
>>>myFunc(1, 2, 3, 4, 5, 6, 7)
(2, 3, 4, 5, 6, 7)
[2, 3, 4, 5, 6, 7]

사전을 전달하려면 :

def myFunc(arg1, **kargs):
   print kargs
>>>myFunc(1, option1=2, option2=3)
{'option2' : 2, 'option1' : 3}

0

이미 훌륭하고 정답이 제공되었습니다. 예를 들어 기본 빈 목록으로 클래스를 만들고 싶을 때 더 아름답다고 생각하는 작업을 작성하기 위해 다른 구문을 제공하고 싶었습니다.

class Node(object):
    def __init__(self, _id, val, parents=None, children=None):
        self.id = _id
        self.val = val
        self.parents = parents if parents is not None else []
        self.children = children if children is not None else []

이 스 니펫은 if else 연산자 구문을 사용합니다. 특히 콜론 등이없는 깔끔한 한 줄짜리이고 거의 일반 영어 문장처럼 읽기 때문에 특히 좋습니다. :)

귀하의 경우에는 쓸 수 있습니다

def myFunc(working_list=None):
    working_list = [] if working_list is None else working_list
    working_list.append("a")
    print working_list

-3

UCSC 확장 수업을 들었습니다. Python for programmer

def Fn (data = []) :

a) 호출 할 때마다 데이터 목록이 비어 있도록하는 것이 좋습니다.

b)는 호출에서 인수를 제공하지 않는 함수에 대한 모든 호출이 데이터로 빈 목록을 가져 오도록하는 좋은 생각입니다.

c) 데이터가 문자열 목록 인 한 합리적인 아이디어입니다.

d)는 기본 []이 데이터를 축적하고 기본 []이 후속 호출로 변경되기 때문에 나쁜 생각입니다.

대답:

d)는 기본 []이 데이터를 축적하고 기본 []이 후속 호출로 변경되기 때문에 나쁜 생각입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.