Python의 중첩 함수


86

다음과 같은 Python 코드로 얻을 수있는 이점이나 의미는 무엇입니까?

class some_class(parent_class):
    def doOp(self, x, y):
        def add(x, y):
            return x + y
        return add(x, y)

나는 이것을 오픈 소스 프로젝트에서 발견했고, 중첩 된 함수 내부에서 유용한 일을했지만 호출하는 것 외에는 아무것도하지 않았다. (실제 코드는 여기 에서 찾을 수 있습니다 .) 누군가 이렇게 코딩 할 수있는 이유는 무엇입니까? 외부의 일반 함수가 아닌 중첩 된 함수 내부에서 코드를 작성하는 데 따른 이점이나 부작용이 있습니까?


4
이것이 당신이 찾은 실제 코드입니까, 아니면 당신이 만든 단순한 예제입니까?
MAK

단순화 된 예입니다. 실제 코드는 여기에서 찾을 수 있습니다 : bazaar.launchpad.net/%7Eopenerp/openobject-server/trunk/...
호삼 알리를

2
HEAD를 가리키는 링크가 이제 정확하지 않습니다. 시도 : bazaar.launchpad.net/~openerp/openobject-server/trunk/annotate/…
Craig McQueen

당신이 맞아요 크레이그. 감사합니다.
Hosam Aly

답변:


113

일반적으로 폐쇄 를 위해 수행합니다 .

def make_adder(x):
    def add(y):
        return x + y
    return add

plus5 = make_adder(5)
print(plus5(12))  # prints 17

내부 함수는 둘러싸는 범위 (이 경우 지역 변수 x) 에서 변수에 액세스 할 수 있습니다 . 둘러싸는 범위에서 변수에 액세스하지 않는 경우 실제로는 범위가 다른 일반 함수일뿐입니다.


5
이를 위해 부분을 선호합니다 plus5 = functools.partial(operator.add, 5). 데코레이터는 클로저에 대한 더 나은 예가 될 것입니다.
Jochen Ritzel

4
감사합니다. 제가 게시 한 스 니펫에서 볼 수 있듯이 여기에서는 그렇지 않습니다. 중첩 함수는 단순히 외부 함수에서 호출됩니다.
Hosam Aly

58

내부 함수 생성이 거의 함수 생성기의 정의 인 함수 생성기를 제외하고 중첩 된 함수를 만드는 이유는 가독성을 높이기 위해서입니다. 외부 함수에 의해서만 호출되는 작은 함수가있는 경우 정의를 인라인하여 해당 함수가 수행하는 작업을 확인하기 위해 건너 뛸 필요가 없습니다. 나중에 함수를 재사용해야하는 경우 항상 내부 메서드를 캡슐화 메서드 외부로 이동할 수 있습니다.

장난감 예 :

import sys

def Foo():
    def e(s):
        sys.stderr.write('ERROR: ')
        sys.stderr.write(s)
        sys.stderr.write('\n')
    e('I regret to inform you')
    e('that a shameful thing has happened.')
    e('Thus, I must issue this desultory message')
    e('across numerous lines.')
Foo()

4
유일한 문제는 내부 함수에 액세스 할 수 없기 때문에 때때로 단위 테스트를 어렵게 만들 수 있다는 것입니다.
Challenger5

1
너무 사랑 스러워요!
테이퍼

1
FWIY @ Challenger5 내부 함수가 개인 클래스 함수와 유사하면 어쨌든 단위 테스트되지 않습니다. 나는 정의를 문제의 메소드에 가깝게 유지하면서 메소드를 더 읽기 쉽게 만들기 위해 이것을 시작했습니다.
Mitchell Currie

27

내부 메서드 사용의 잠재적 이점 중 하나는 인수로 전달하지 않고도 외부 메서드 지역 변수를 사용할 수 있다는 것입니다.

def helper(feature, resultBuffer):
  resultBuffer.print(feature)
  resultBuffer.printLine()
  resultBuffer.flush()

def save(item, resultBuffer):

  helper(item.description, resultBuffer)
  helper(item.size, resultBuffer)
  helper(item.type, resultBuffer)

다음과 같이 쓸 수 있습니다.

def save(item, resultBuffer):

  def helper(feature):
    resultBuffer.print(feature)
    resultBuffer.printLine()
    resultBuffer.flush()

  helper(item.description)
  helper(item.size)
  helper(item.type)

8

그런 코드에 대한 좋은 이유를 상상할 수 없습니다.

다른 Ops와 마찬가지로 이전 버전의 내부 기능에 대한 이유가있을 수 있습니다.

예를 들어, 이것은 약간 더 의미가 있습니다.

class some_class(parent_class):
    def doOp(self, op, x, y):
        def add(x, y):
            return x + y
        def sub(x,y):
            return x - y
        return locals()[op](x,y)

some_class().doOp('add', 1,2)

그러나 내부 함수는 대신 ( "private") 클래스 메서드 여야합니다.

class some_class(object):
    def _add(self, x, y):
        return x + y
    def doOp(self, x, y):
        return self._add(x,y)

예, 아마도 doOp가 인수에 사용할 연산자를 지정하기 위해 문자열을 가져 왔을 것입니다 ...
Skilldrick 2009-10-19

1
@ArtOfWarfare는 단순히 모듈을 사용하는 것이 좋습니다. 클래스는 주를위한 것입니다.
aehlke 2013 년

6

로컬 메소드의 개념은 로컬 변수와 유사합니다. 더 큰 네임 스페이스를 오염시키지 마십시오. 대부분의 언어는 이러한 기능을 직접 제공하지 않기 때문에 분명히 이점이 제한됩니다.


1

코드가 정확히 이와 같았습니까? 이와 같은 작업을 수행하는 일반적인 이유는 베이크 인 매개 변수가있는 함수 인 부분을 생성하기위한 것입니다. 외부 함수를 호출하면 매개 변수가 필요없는 콜 러블이 반환되므로 매개 변수를 전달할 수없는 곳에서 저장하고 사용할 수 있습니다. 그러나 게시 한 코드는 그렇게하지 않습니다. 즉시 함수를 호출하고 콜 러블이 아닌 결과를 반환합니다. 본 실제 코드를 게시하는 것이 유용 할 수 있습니다.


1
내 질문에 대한 의견에 원본 코드에 대한 링크를 추가했습니다. 보시다시피 제 것은 간단한 예이지만 여전히 거의 동일합니다.
Hosam Aly
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.