"적어도 놀랍지"와 변경 가능한 기본 인수


질문

 

파이썬으로 땜질하는 사람은 다음과 같은 문제에 따라 물린 (또는 조각에 찢어 지거나 조각이 찢어졌습니다).

def foo(a=[]):
    a.append(5)
    return a

파이썬 초보자리는이 기능이 항상 하나의 요소 만있는 목록을 항상 반환 할 것으로 기대합니다. [5].그 결과는 대신 매우 다르며 매우 놀랍습니다 (초보자를 위해) :

>>> foo()
[5]
>>> foo()
[5, 5]
>>> foo()
[5, 5, 5]
>>> foo()
[5, 5, 5, 5]
>>> foo()

광산 관리자는 한 번이 기능으로 첫 만남을했으며 언어의 "극적인 디자인 결함"이라고 불렀습니다.나는 그 행동이 근본적인 설명이 있었다고 대답했고, 인테리어를 이해하지 못하면 실제로는 매우 수수께끼가되고 예상치 못한 것입니다.그러나 나는 다음 질문에 대답 할 수 없었습니다. 함수 정의에서 기본 인수를 바인딩하는 이유는 무엇입니까?나는 경험 많은 행동이 실제로 실제로 실제로 실제로 사용하지 않고 정적 변수를 정적으로 사용합니까?

편집하다:

BACZEK는 흥미로운 예를 만들었습니다.대부분의 의견과 UTAAL의 대부분의 대부분과 함께, 나는 더 이상 정교했습니다 :

>>> def a():
...     print("a executed")
...     return []
... 
>>>            
>>> def b(x=a()):
...     x.append(5)
...     print(x)
... 
a executed
>>> b()
[5]
>>> b()
[5, 5]

나에게 설계 결정이 파라미터의 범위를 배치 할 위치에 비례하는 것처럼 보입니다. 기능 내부, 또는 "함께"

함수 안의 바인딩을 수행하면 기능이 호출되지 않거나 정의되지 않은 것으로 지정되지 않은 기본값에 효과적으로 바인딩되어 있음을 의미합니다. DEED 결함을 나타내는 무언가 : DEF 라인은 바인딩의 일부가 "하이브리드"가 될 것입니다.(함수 객체의)는 정의에서 발생하며 함수 호출 시간에 파트 (기본 매개 변수 할당)가 발생합니다.

실제 동작은 더 일관성이 있습니다. 해당 줄의 모든 것이 함수 정의에서 의미가있는 경우 해당 줄이 실행될 때 평가됩니다.


답변

 

사실, 이것은 디자인 결함이 아니며 내부 또는 성능 때문이 아닙니다. 파이썬의 함수가 첫 번째 클래스 오브젝트이며 코드 조각이 아니라는 사실에서 간단히옵니다.

이런 식으로 생각하자마자이면 완전히 의미가 있습니다. 함수는 정의에서 평가되는 객체입니다.기본 매개 변수는 "멤버 데이터"의 종류이므로 해당 상태가 한 호출에서 다른 호출에서 다른 객체와 정확하게 변경 될 수 있습니다.

어쨌든, effbot은 파이썬의 기본 매개 변수 값 에서이 동작의 이유에 대한 아주 좋은 설명을 가지고 있습니다. 나는 그것을 매우 분명히 발견했고, 나는 기능 물체가 어떻게 작동하는지 더 나은 지식을 위해 그것을 읽는 것을 정말로 제안한다.



답변

다음 코드가 있다고 가정 해보십시오

fruits = ("apples", "bananas", "loganberries")

def eat(food=fruits):
    ...

내가 먹는 선언을 볼 때 가장 놀랄만한 것은 첫 번째 매개 변수가 주어지지 않으면 튜플 ( "사과", "바나나", "LogAnberries")과 동일 할 것이라고 생각하는 것입니다.

그러나 나중에 코드에서 가정 해보십시오.

def some_random_function():
    global fruits
    fruits = ("blueberries", "mangos")

그런 다음 기본 매개 변수가 함수 선언이 아닌 함수 실행에서 바인딩 된 경우 과일이 변경되었음을 발견하기 위해 매우 나쁜 방식으로 깜짝 놀랐습니다.위의 foo 함수가 목록을 돌연변이라는 것을 발견하는 것보다 더 놀랄 것입니다.

실제 문제는 변경 가능한 변수로 거짓말을하고 모든 언어에는이 문제가 어느 정도 있습니다.질문은 다음과 같습니다. Java에서 다음 코드가 있다고 가정하십시오.

StringBuffer s = new StringBuffer("Hello World!");
Map<StringBuffer,Integer> counts = new HashMap<StringBuffer,Integer>();
counts.put(s, 5);
s.append("!!!!");
System.out.println( counts.get(s) );  // does this work?

이제 MAP가지도에 배치되면 MAP가 StringBuffer 키의 값을 사용하거나 키를 참조로 키를 저장합니까?어느 쪽이든, 누군가는 놀랍습니다.그들이 사용하는 열쇠가 문자 그대로 동일하게 사용 되더라도 자신의 개체를 검색 할 수없는 값을 사용하여지도에서 객체를 얻으려고 한 사람.그것을지도에 넣는 데 사용 된 객체 (실제로 파이썬이 사전 키로 사용되는 내장 데이터 유형을 사용할 수 없게하는 이유입니다).

귀하의 예는 파이썬 신입자가 놀랍고 물린 경우의 좋은 사례 중 하나입니다.그러나 나는 우리가 "고정되어있는"이면, 그 대신에 물릴 수있는 다른 상황 만 만들고, 그 사람은 덜 직관적 일 것입니다.더욱이, 이것은 항상 변경 가능한 변수를 다룰 때의 경우이다.당신은 항상 누군가가 자신이 쓰는 코드에 따라 어떤 코드에 따라 어떤 코드를 직관적으로 하나 또는 반대 행동을 기대할 수있는 경우를 항상 실행합니다.

개인적으로 Python의 현재 접근 방식은 다음과 같습니다. 기능이 정의되고 해당 객체가 항상 기본값 인 경우 기본 함수 인수가 평가됩니다.나는 그들이 빈 목록을 사용하여 특별한 사례를 할 수 있다고 생각하지만, 그 종류의 특별한 케이스는 더욱 놀라운 일이 일어나지 않을 것입니다.



답변

문서의 관련 부분 :

기본 매개 변수 값은 함수 정의가 실행될 때 왼쪽에서 오른쪽으로 평가됩니다.즉, 함수가 정의되면 표현식이 한 번 평가되고 동일한 "미리 계산 된"값이 각 호출에 사용됩니다.이것은 기본 매개 변수가 목록 또는 사전과 같은 변경 가능한 객체 인시기를 이해하는 것이 특히 중요합니다. 함수가 객체를 수정하는 경우 (예를 들어 목록에 항목을 추가하여) 기본값이 적용됩니다.이것은 일반적으로 의도 된 것이 아닙니다.이 주위의 방법은 기본값으로 없음을 사용하고 함수의 본문에서 명시 적으로 테스트하는 것입니다. def whats_on_the_telly (penguin = none) : 펭귄이 없으면 : 펭귄 = [] penguin.append ( "동물원의 속성") 펭귄을 돌려주십시오



답변

나는 파이썬 인터프리터 내부 작동 (그리고 나는 컴파일러와 통역사의 전문가가 아닙니다)에 대해서는 아무 것도 불필요하거나 불가능한 것을 제안하면 나를 비난하지 마십시오.

파이썬 객체가 변경할 수있는 것으로 나타났습니다. 기본 인수를 설계 할 때 이것이 고려되어야한다고 생각합니다. 목록을 인스턴스화 할 때 :

a = []

A가 참조하는 새 목록을 얻을 수 있습니다.

왜 A = []인가?

def x(a=[]):

기능 정의에 새 목록을 인스턴스화하며 호출되지 않으려면? "사용자가 인수를 제공하지 않으면 새 목록을 인스턴스화하고 호출자가 생성 한 것처럼 사용하는 것처럼"묻는 경우 "묻는 것과 같습니다. 나는 이것이 대신 모호하다고 생각한다 :

def x(a=datetime.datetime.now()):

사용자, x를 정의하거나 실행할 때 해당 DateTime에 기본값으로 원하도록 원하십니까? 이 경우 이전에 기본 인수 "할당"이 함수의 첫 번째 명령 인 것처럼 동일한 동작을 유지합니다 (DateTime.now ()은 함수 호출에서 호출됩니다). 다른 한편으로, 사용자가 정의 타임 매핑을 원한다면 그는 쓸 수 있습니다.

b = datetime.datetime.now()
def x(a=b):

알아, 알아, 그건 폐쇄입니다.또는 파이썬은 정의 시간 바인딩을 강제하는 키워드를 제공 할 수 있습니다.

def x(static a=b):


답변

음, 그 이유는 코드가 실행될 때 바인딩이 수행되며 기능이 정의되면 기능 정의가 실행됩니다.

이것을 비교하십시오 :

class BananaBunch:
    bananas = []

    def addBanana(self, banana):
        self.bananas.append(banana)

이 코드는 똑같은 예상치 못한 사례를 겪습니다.바나나는 클래스 속성이므로 작업을 추가 할 때 해당 클래스의 모든 인스턴스에 추가됩니다.그 이유는 정확히 동일합니다.

그것은 단지 "작동 방식"이며 함수 사례에서 다르게 작동하는 것은 아마도 복잡 할 것입니다. 그리고 클래스의 경우 수업 코드를 유지하거나 적어도 많은 객체 인스턴스화를 늦추어야 할 것입니다.객체가 생성 될 때 실행합니다.

예, 예상치 못한 것입니다.그러나 페니가 떨어지면 Python이 일반적으로 작동하는 방식으로 완벽하게 맞습니다.사실, 좋은 가르침 원조이며, 왜이 이유를 이해하면 파이썬을 훨씬 더 잘 이해할 수 있습니다.

그것은 좋은 파이썬 자습서에서 눈에 띄게 특징이어야한다고 말했습니다.당신이 언급 할 때, 모두는이 문제가 빨리 또는 나중에 실행됩니다.



답변

왜 안내하지 않는거야?

통화 전화에 Python (2 및 3 Apply)이 제공하는 통찰력있는 내성을 수행하지 않은 통찰력이없는 내성을 수행하지 않았습니다.

다음과 같이 정의 된 간단한 작은 기능 FUNC가 주어졌습니다.

>>> def func(a = []):
...    a.append(5)

Python이 발생하면이 기능에 대한 코드 객체를 만들려면 첫 번째 작업이 컴파일됩니다.이 컴파일 단계가 완료되었지만 Python은 *를 평가 한 다음 함수 객체 자체에 기본 인수 (여기에 빈 목록 [] 여기)를 저장합니다.언급 된 상위 응답으로 : 목록 A는 이제 기능 FUNC의 구성원으로 간주 될 수 있습니다.

따라서 목록이 기능 객체 내에서 확장되는 방법을 검사하기 전과 후에 일부 내역을 수행 해 봅시다.Python 2의 경우 Python 3.x를 사용하고 있습니다 (__defaults__ 또는 python 2에서 func_defaults를 사용하십시오. 예, 똑같은 두 개의 이름).

실행 전 :

>>> def func(a = []):
...     a.append(5)
...     

파이썬 이이 정의를 실행하면 기본 매개 변수가 지정된 기본 매개 변수 (여기에 A = [] 여기에)와 함수 오브젝트의 __defaults__ 특성 (해당 섹션 : 호출 가능)에서이를 곱합니다.

>>> func.__defaults__
([],)

o.k, __defaults__의 단일 항목으로 빈 목록은 예상대로 __defaults__입니다.

실행 후 기능 :

이제이 기능을 실행하겠습니다.

>>> func()

이제 __defaults__ 다시 봅시다.

>>> func.__defaults__
([5],)

놀랐습니까?객체 내의 값이 변경됩니다!이제이 내장 된 LIST 객체에이를 추가하기 만하면 함수에 대한 연속적인 통화가 단순히 다음과 같습니다.

>>> func(); func(); func()
>>> func.__defaults__
([5, 5, 5, 5],)

따라서이 '결함'이 일어나는 이유는 기본 인수가 함수 개체의 일부이기 때문입니다.여기에 이상한 일은 아무것도 없어, 그것은 모두 조금 놀라운 일입니다.

전투에 대한 공통 솔루션은 기본값으로 없음을 사용하고 함수 본문에서 초기화하는 것입니다.

def func(a = None):
    # or: a = [] if a is None else a
    if a is None:
        a = []

함수 본문이 매번 AWEW가 실행되므로 인수가 전달되지 않은 경우 항상 새로운 새로운 빈 목록을 얻을 수 있습니다.


__defaults__의 목록이 함수 FUNC에서 사용되는 것과 동일한 지 확인하려면 함수를 변경하여 함수 본문에서 사용 된 목록의 ID를 반환 할 수 있습니다.그런 다음 __defaults__ (__defaults__)의 위치 [0]의 목록과 비교하면 실제로 동일한 목록 인스턴스를 참조하는 방법이 표시됩니다.

>>> def func(a = []): 
...     a.append(5)
...     return id(a)
>>>
>>> id(func.__defaults__[0]) == func()
True

모두 내독력의 힘을 가진 것!


* Python이 함수 편집 중에 기본 인수를 평가하는지 확인하려면 다음을 실행 해보십시오.

def bar(a=input('Did you just see me without calling the function?')): 
    pass  # use raw_input in Py2

알아 차릴 때, input ()이 함수를 작성하고 이름 표시 줄에 바인딩하는 프로세스가 만들어졌습니다.

출처:https://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument