문제

알고리즘에서 문제의 근본 원인을 이해하기가 매우 어려웠습니다. 그런 다음 기능을 단계별로 단순화함으로써 Python에서 기본 인수 평가가 내가 예상대로 작동하지 않는다는 것을 알았습니다.

코드는 다음과 같습니다.

class Node(object):
    def __init__(self, children = []):
        self.children = children

문제는 노드 클래스의 모든 인스턴스가 동일하게 공유한다는 것입니다. children 속성에 속성에 명시 적으로 주어지지 않은 경우 :

>>> n0 = Node()
>>> n1 = Node()
>>> id(n1.children)
Out[0]: 25000176
>>> id(n0.children)
Out[0]: 25000176

이 디자인 결정의 논리를 이해하지 못합니까? Python Designers는 왜 정의 시간에 기본 인수를 평가해야한다고 결정한가? 이것은 나에게 매우 반 직관적 인 것 같습니다.

도움이 되었습니까?

해결책

대안은 함수 객체에 "기본 인수 값"을 저장하여 함수가 해당 인수에 대한 지정된 값없이 호출 될 때마다 반복해서 실행할 코드의 "덩어리"로 저장하는 상당히 헤비급입니다. 초기 바인딩 (DEF 시간에 바인딩)을 얻는 것이 훨씬 어렵습니다. 예를 들어, 파이썬에서 존재하는 경우 :

def ack(m, n, _memo={}):
  key = m, n
  if key not in _memo:
    if m==0: v = n + 1
    elif n==0: v = ack(m-1, 1)
    else: v = ack(m-1, ack(m, n-1))
    _memo[key] = v
  return _memo[key]

... 위와 같은 메모 화 된 기능을 작성하는 것은 상당히 기본 작업입니다. 비슷하게:

for i in range(len(buttons)):
  buttons[i].onclick(lambda i=i: say('button %s', i))

... 단순 i=i, 기본 arg 값의 조기 바인딩 (정의 시간)에 의존하는 것은 조기 바인딩을 얻는 사소한 간단한 방법입니다. 따라서 현재 규칙은 간단하고 간단하며 설명하고 이해하기 쉬운 방식으로 원하는 모든 것을 할 수 있습니다. 표현식의 값을 늦게 바인딩하려면 기능 본문에서 그 표현을 평가하십시오. 조기 바인딩을 원한다면 ARG의 기본값으로 평가하십시오.

두 상황 모두에 대한 늦은 바인딩을 강요하는 대안은 이러한 유연성을 제공하지 않을 것이며, 위의 예에서와 같이 조기 바인딩이 필요할 때마다 후프 (예 : 폐쇄 공장으로 기능을 포장)를 통과하도록 강요 할 것입니다. 이 가상의 디자인 결정에 의해 프로그래머에게 강력한 무거운 가중 보일러 플레이트 (전체적으로 펑크를 생성하고 반복적으로 평가하는 것 이상).

다시 말해서, "하나만, 바람직하게는 하나의 명백한 방법이 있어야합니다 [1]: 늦은 바인딩을 원할 때 이미이를 달성하는 완벽하게 명백한 방법이 있습니다 (모든 함수의 코드는 모두 실행되기 때문에 콜 타임에 분명히 모든 것이 평가되었습니다 거기 늦은 부분); 기본 ARG 평가를 통해 조기 바인딩을 생성하면 조기 바인딩을 얻는 두 가지 명백한 방법과 조기 바인딩을 얻는 명백한 방법 (마이너스!-)을 얻는 것이 아니라 초기 바인딩을 달성 할 수있는 명백한 방법을 제공합니다.

1] : "네덜란드어가 아니라면 처음에는 분명하지 않을 수 있습니다."

다른 팁

문제는 이것입니다.

이니셜 라이저로 기능을 평가하기에는 너무 비쌉니다. 함수가 호출 될 때마다.

  • 0 간단한 문자입니다. 한 번 평가하고 영원히 사용하십시오.

  • int 이 초기화로 필요할 때마다 평가 해야하는 함수 (목록)입니다.

구성 [] 문자 적입니다 0, 그것은 "이 정확한 대상"을 의미합니다.

문제는 어떤 사람들은 그것이 의미하기를 희망한다는 것입니다. list "이 기능을 평가하십시오.

필요한 것을 추가하는 것은 부담이 될 것입니다. if 이 평가를 항상 수행하는 진술. 모든 인수를 리터럴로 취하고 함수 평가를 시도하는 일의 일부로 추가 기능 평가를 수행하지 않는 것이 좋습니다.

또한 더 근본적으로 기술적으로는 기술적입니다 불가능한 인수 기본값을 기능 평가로 구현합니다.

이런 종류의 원형에 대한 재귀적인 공포를 잠시 생각해보십시오. 기본값이 리터럴 인 대신 매개 변수의 기본값이 필요할 때마다 평가되는 함수가 될 수 있다고 가정 해 봅시다.

이것은 길과 평행을 이룰 것입니다 collections.defaultdict 공장.

def aFunc( a=another_func ):
    return a*2

def another_func( b=aFunc ):
    return b*3

의 가치는 무엇입니까 another_func()? 기본값을 얻으려면 b, 그것은 평가해야합니다 aFunc, 평가가 필요합니다 another_func. 죄송합니다.

물론 당신의 상황에서는 이해하기가 어렵습니다. 그러나 매번 기본 args를 평가하면 시스템에 런타임 부담이 심할 것입니다.

또한 컨테이너 유형의 경우이 문제가 발생할 수 있음을 알고 있어야합니다. 그러나 그 일을 명시 적으로 만들어서 우회 할 수 있습니다.

def __init__(self, children = None):
    if children is None:
       children = []
    self.children = children

이것에 대한 해결 방법, 여기에서 논의했습니다 (그리고 매우 견고함), IS :

class Node(object):
    def __init__(self, children = None):
        self.children = [] if children is None else children

Von Löwis로부터 답을 찾는 이유는 있지만, 함수 정의가 Python의 아키텍처로 인해 코드 객체를 만들기 때문에 기본 인수에서 이와 같은 참조 유형으로 작업 할 수있는 기능이 없기 때문일 수 있습니다.

파이썬이 기본 인수를 어떻게 구현하는지 알기 전까지는 이것이 반 직관적이라고 생각했습니다.

함수는 객체입니다. 로드 시간에 Python은 함수 객체를 생성하고의 기본값을 평가합니다. def 진술, 그것들을 튜플에 넣고 그 튜플을 지명 된 함수의 속성으로 추가합니다. func_defaults. 그런 다음 함수가 호출되면 호출이 값을 제공하지 않으면 파이썬은 기본값을 가져옵니다. func_defaults.

예를 들어:

>>> class C():
        pass

>>> def f(x=C()):
        pass

>>> f.func_defaults
(<__main__.C instance at 0x0298D4B8>,)

그래서 모든 전화 f 인수를 제공하지 않는 것은 동일한 인스턴스를 사용합니다. C, 그것은 기본값이기 때문에.

Python이 왜 이런 식으로 하는가 : 글쎄, 그 튜플 ~할 수 있었다 기본 인수 값이 필요할 때마다 호출되는 함수를 포함합니다. 즉시 명백한 성능 문제와는 별도로, 불필요한 기능 호출을 피하기 위해 비전 유형의 함수 대신 문자 그대로 값을 저장하는 것과 같은 특별한 경우의 우주에 들어가기 시작합니다. 물론 성능의 영향이 풍부합니다.

실제 행동은 정말 간단합니다. 그리고 당신이 원하다 런타임에 함수 호출로 생성 할 기본값 :

def f(x = None):
   if x == None:
      x = g()

이것은 구문 및 실행 단순성에 대한 Python의 강조에서 비롯됩니다. DEF 문은 실행 중에 특정 지점에서 발생합니다. Python 통역사가 그 지점에 도달하면 해당 줄의 코드를 평가 한 다음 함수 본문에서 코드 객체를 작성하여 기능을 호출 할 때 나중에 실행됩니다.

함수 선언과 기능 본문 사이의 간단한 분할입니다. 선언은 코드에서 도달하면 실행됩니다. 신체는 통화 시간에 실행됩니다. 선언은 도달 할 때마다 실행되므로 루핑을 통해 여러 기능을 생성 할 수 있습니다.

funcs = []
for x in xrange(5):
    def foo(x=x, lst=[]):
        lst.append(x)
        return lst
    funcs.append(foo)
for func in funcs:
    print "1: ", func()
    print "2: ", func()

함수 선언이 실행될 때마다 별도의 목록이 생성 된 5 가지 별도의 기능이 만들어졌습니다. 각 루프에서 funcs, 매번 동일한 목록을 사용하여 동일한 함수가 각 패스에서 두 번 실행됩니다. 결과를 제공합니다.

1:  [0]
2:  [0, 0]
1:  [1]
2:  [1, 1]
1:  [2]
2:  [2, 2]
1:  [3]
2:  [3, 3]
1:  [4]
2:  [4, 4]

다른 사람들은 당신에게 param = none을 사용하고 값이 없으면 신체에 목록을 할당하는 해결 방법을 제공했습니다. 약간 추악하지만 단순성은 강력하며 해결 방법은 너무 고통스럽지 않습니다.

추가로 편집 : 이에 대한 자세한 내용은 Effbot의 기사를 참조하십시오. http://effbot.org/zone/default-values.htm, 및 언어 참조, 여기 : http://docs.python.org/reference/compound_stmts.html#function

파이썬 기능 정의는 다른 모든 코드와 마찬가지로 코드입니다. 그들은 일부 언어가있는 방식으로 "마법"이 아닙니다. 예를 들어, Java에서는 "Now"를 "나중에"정의한 것으로 참조 할 수 있습니다.

public static void foo() { bar(); }
public static void main(String[] args) { foo(); }
public static void bar() {}

그러나 파이썬에서

def foo(): bar()
foo()   # boom! "bar" has no binding yet
def bar(): pass
foo()   # ok

따라서 기본 인수는 현재 해당 코드 라인이 평가 된 순간에 평가됩니다!

그들이 있다면 누군가가 왜 다른 방법이 아닌지 묻는 질문을 게시 할 것입니다.

지금 그들이 가지고 있다고 가정 해 봅시다. 필요한 경우 현재 동작을 어떻게 구현 하시겠습니까? 함수 내에서 새 객체를 만들기는 쉽지만 객체를 "고정시킬 수는 없습니다"(삭제할 수는 있지만 동일하지는 않습니다).

다른 게시물의 주요 인수를 부가함으로써 반대 의견을 제시 할 것입니다.

기능이 실행될 때 기본 인수를 평가하면 성능이 좋지 않습니다.

나는 이것이 믿기 어렵다는 것을 안다. 기본 인수 할당과 같은 경우 foo='some_string' 용납 할 수없는 양의 오버 헤드를 실제로 추가하면 불변의 리터럴에 대한 과제를 식별하고 사전 압축 할 수있을 것입니다.

변한 객체와 같은 기본 할당을 원한다면 foo = [], 그냥 사용하십시오 foo = None, 그 뒤에 foo = foo or [] 기능 본문에서.

이것은 개별 사례에서 비공식적 일 수 있지만 디자인 패턴 으로서는 매우 우아하지 않습니다. 보일러 플레이트 코드를 추가하고 기본 인수 값을 가리 킵니다. 같은 패턴 foo = foo or ... 만약 작동하지 마십시오 foo 정의되지 않은 진실 값을 가진 Numpy 배열과 같은 개체 일 수 있습니다. 그리고 어디에서 None 의도적으로 전달 될 수있는 의미있는 인수 값이며, 센티넬로 사용할 수 없으며이 해결 방법은 정말 추악 해집니다.

현재 동작은 ~해야 한다 공유 accross 기능 호출.

나는 반대의 증거를 보게되어 기쁘지만, 내 경험상이 사용 사례는 함수를 호출 할 때마다 새로 만들어야 할 변한 객체보다 훨씬 덜 빈번합니다. 나에게 그것은 또한 더 진보 된 사용 사례처럼 보이지만 빈 컨테이너가있는 우발적 기본값 지정은 새로운 Python 프로그래머에게는 일반적인 gotcha입니다. 따라서 최소의 놀라움의 원칙은 함수가 실행될 때 기본 인수 값을 평가해야한다는 것을 암시합니다.

또한 함수 호출에서 공유 해야하는 변이 가능한 객체에 대한 쉬운 해결 방법이있는 것 같습니다. 함수 외부에서 초기화하십시오.

그래서 나는 이것이 나쁜 디자인 결정이라고 주장합니다. 제 생각에는 구현이 실제로 더 간단하고 유효한 (제한된) 사용 사례가 있기 때문에 선택되었을 것입니다. 불행히도, 나는 핵심 파이썬 개발자가 Python 3이 소개 한 뒤로 비 호환성의 양을 반복하지 않기를 원하기 때문에 이것이 바뀌지 않을 것이라고 생각합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top