기능 A가 기능 B에만 필요한 경우 A를 B 내부에 정의해야합니까? [닫은]


147

간단한 예입니다. 하나는 다른 것에서 호출되는 두 가지 방법 :

def method_a(arg):
    some_data = method_b(arg)

def method_b(arg):
    return some_data

파이썬에서 우리는 def다른 것을 선언 할 수 있습니다 def. 따라서에 method_b필요하고 from 만 호출하는 경우 inside method_a선언해야 합니까? 이처럼 :method_bmethod_a

def method_a(arg):

    def method_b(arg):
        return some_data

    some_data = method_b(arg)

아니면 이것을 피해야합니까?


7
정말 펑키 한 것을하지 않는 한 다른 함수를 정의 할 필요가 없습니다. 당신이 뭘 하려는지 우리가 더 도움이 답변을 제공 할 수 있도록하지만,에 자세히 설명해
inspectorG4dget

6
전화 하지 않기 때문에 두 번째 예가 다르다는 것을 알고 method_b있습니까? (@inspector : 엄밀히 말하면, 약간의 함수형 프로그래밍, 특히 클로저에 들어갈 때 매우 유용합니다).

3
@delnan : 나는 "당신은 당신이 의미 생각 하지 않습니다 ... 엄밀히 말하면,하지만, 필요"
마티

4
내부 함수의 사용 사례는 https://realpython.com/blog/python/inner-functions-what-are-they-good-for/ 링크에 훌륭하게 요약되어 있습니다 . 용도에 맞지 않는 경우 피하는 것이 좋습니다.

1
좋은 질문이지만 실제 답변이없는 것 같습니다 ...
Mayou36

답변:


136
>>> def sum(x, y):
...     def do_it():
...             return x + y
...     return do_it
... 
>>> a = sum(1, 3)
>>> a
<function do_it at 0xb772b304>
>>> a()
4

이것이 당신이 찾고 있었던 것입니까? 이를 클로저 라고합니다 .


14
이것은 훨씬 더 나은 설명입니다. 내 답변을 삭제했습니다
pyfunc

왜 def sum (x, y) : x + y를 반환하지 않습니까?
망고

4
@mango : 이것은 개념을 전달하는 장난감의 예일뿐입니다. 실제로 실제 사용 do_it()에서는 단일 return명령문 에서 일부 산술로 처리 할 수있는 것보다 약간 더 복잡한 것이있을 것 입니다.
martineau

2
@mango 약간 더 유용한 예를 들어 질문에 대답했습니다. stackoverflow.com/a/24090940/2125392
CivFan

10
질문에 대답하지 않습니다.
Hunsu

49

실제로 이렇게하면 많은 것을 얻지 못합니다. 실제로 method_a호출 될 때마다 다른 함수를 정의하고 다시 컴파일하기 때문에 속도가 느려집니다 . 따라서 함수 이름 앞에 밑줄을 붙여서 개인 메서드임을 나타내는 것이 _method_b좋습니다.

중첩 된 함수의 정의가 어떤 이유로 매번 바뀌면이 작업을 수행하려고하지만 디자인의 결함을 나타낼 있다고 가정합니다 . 즉이 말했다 이다 중첩 함수가 외부 함수에 전달하지만, 명시 적으로 예를 들어, 함수 장식을 작성할 때 종종 발생하는 그들에게 전달되지 않은 인수를 사용할 수 있도록이 작업을 수행 할 수있는 타당한 이유가. 데코레이터를 정의하거나 사용하지 않아도 허용되는 답변에 표시되는 내용입니다.

최신 정보:

이 사소한 경우는별로 인정하지 않지만 중첩하는 것이 느리다는 것을 증명합니다 (Python 3.6.1 사용).

setup = """
class Test(object):
    def separate(self, arg):
        some_data = self._method_b(arg)

    def _method_b(self, arg):
        return arg+1

    def nested(self, arg):

        def method_b2(self, arg):
            return arg+1

        some_data = method_b2(self, arg)

obj = Test()
"""
from timeit import Timer
print(min(Timer(stmt='obj.separate(42)', setup=setup).repeat()))  # -> 0.24479823284461724
print(min(Timer(stmt='obj.nested(42)', setup=setup).repeat()))    # -> 0.26553459700452575

참고 나는 self실제 함수와 비슷하게 만들기 위해 샘플 함수 에 몇 가지 인수를 추가했습니다 ( method_b2아직 기술적으로 Test클래스 의 메소드는 아니지만 ). 또한 중첩 함수는 실제로는 해당 버전에서 호출되지 않습니다.


21
약간의 시간이 걸리는 함수 객체를 만들어야하지만 외부 함수가 호출 될 때마다 실제로 내부 함수를 완전히 컴파일하지는 않습니다. 반면에 함수 이름은 전역 이름이 아닌 로컬 이름이되므로 함수를 호출하는 것이 더 빠릅니다. 나의 시련에서, 시간적으로 그것은 기본적으로 대부분의 시간을 씻는 것이다. 여러 번 호출하면 내부 함수로 더 빠를 수도 있습니다.
kindall

7
예, 내부 함수를 여러 번 호출해야합니다. 루프에서 호출하거나 몇 번 이상 호출하면 함수의 로컬 이름을 갖는 이점이 함수 작성 비용보다 중요합니다. 내 시험에서 이것은 내부 함수를 약 3-4 번 호출 할 때 발생합니다. 물론 함수의 로컬 이름을 정의하여 (예 : 비용이 거의 들지 않음) 동일한 속성을 얻을 수 있습니다. method_b = self._method_b그런 다음 method_b반복되는 속성 조회를 피하기 위해 호출 합니다. (최근에 많은 시간을
쏟아 부었습니다

3
@kindall : 네, 맞습니다. 타이밍 테스트를 수정하여 보조 기능을 30 번 호출하고 결과가 바뀌 었습니다. 이 답장을 볼 기회가 있으면 답변을 삭제하겠습니다. 깨달음에 감사드립니다.
martineau

2
그럼에도 불구하고 유익한 답변이므로 내 -1을 설명하고 싶었습니다. 사소한 성능 차이에 중점을두기 때문에이 -1을주었습니다 (대부분의 시나리오에서 코드 객체 생성은 함수의 실행 시간의 일부만 소요됩니다) ). 이 경우 고려해야 할 중요한 점은 인라인 함수가 코드 가독성과 유지 관리 성을 향상시킬 수 있는지 여부입니다. 관련 코드를 찾기 위해 파일을 검색 / 스크롤 할 필요가 없기 때문에 자주 발생한다고 생각합니다.
Blixt

2
@Blixt : 동일한 클래스의 다른 메소드가 중첩되지 않은 경우에도 동일한 클래스의 다른 메소드가 다른 클래스의 메소드와 "원거리"가 될 가능성이 거의 없기 때문에 논리에 결함이 있다고 생각합니다. 다른 파일에 있어야합니다). 또한 내 대답의 첫 번째 문장은 "이것으로 실제로 많은 것을 얻지 못한다"는 사소한 차이점이라고 지적합니다.
martineau

27

함수 내부의 함수는 일반적으로 클로저에 사용됩니다 .

( 클로저를 정확하게 클로저로 만드는 것에 대한 많은 논쟁 이 있습니다.)

다음은 내장을 사용하는 예입니다 sum(). start한 번 정의 하고 그때부터 사용합니다.

def sum_partial(start):
    def sum_start(iterable):
        return sum(iterable, start)
    return sum_start

사용:

>>> sum_with_1 = sum_partial(1)
>>> sum_with_3 = sum_partial(3)
>>> 
>>> sum_with_1
<function sum_start at 0x7f3726e70b90>
>>> sum_with_3
<function sum_start at 0x7f3726e70c08>
>>> sum_with_1((1,2,3))
7
>>> sum_with_3((1,2,3))
9

내장 파이썬 폐쇄

functools.partial 폐쇄의 예입니다.

python docs 에서 대략 다음과 같습니다.

def partial(func, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = keywords.copy()
        newkeywords.update(fkeywords)
        return func(*(args + fargs), **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc

(아래 답변은 @ user225312에게 문의하십시오.이 예제는 이해하기 쉽고, @mango의 의견에 답변하는 데 도움이 될 것입니다.)


-1 예, 일반적으로 클로저로 사용됩니다. 이제 질문을 다시 읽으십시오. 그는 기본적으로 그가 보여주는 개념이 사례에 사용될 수 있는지 물었다. b. 그에게 종종 경우 a에 사용된다고 말하는 것은 나쁜 대답이 아니라이 질문에 대한 잘못된 대답입니다. 예를 들어 캡슐화를 위해 위의 작업을 수행하는 것이 좋은지 여부에 관심이 있습니다.
Mayou36

@ Mayou36 공정하게 말하면, 모든 질문에 대답하려고하기보다는 문제에 대해 개방적입니다. 나는 한 가지에 집중하는 것이 최선이라고 생각했습니다. 또한 질문은 명확하지 않습니다. 예를 들어, 두 번째 예에서는 의미가 없더라도 닫힘을 의미합니다.
CivFan

@ Mayou36 "그는 기본적으로 그가 보여주는 개념 이 사례 b에 사용될 있는지 물었다 ." 아니요, 질문은 사용 해야하는지 묻습니다 . OP는 이미 사용할 수 있다는 것을 알고 있습니다.
CivFan

1
"method_b가 method_a에만 필요하고 method_a에서만 호출되는 경우 method_a 내에 method_b를 선언해야합니까?" 명확하고 클로저와 관련이 없습니다. 그래, 난 내가 사용 동의 캔을 하는 방식으로 해야한다 . 그러나 그것은 폐쇄와 관련이 없습니다 ... 나는 단지 폐쇄에 대한 많은 답변과 OP가 완전히 다른 질문을 할 때 폐쇄를 사용하는 방법에 놀랐습니다.
Mayou36

@ Mayou36 그것은 당신이 그것을 어떻게 보는지 질문으로 바꾸고 다른 질문을 열어서 해결할 수 있습니다.
CivFan

17

일반적으로 아니요, 함수 내부에 함수를 정의하지 마십시오.

당신이 정말로 좋은 이유가 없다면 당신은하지 않습니다.

왜 안돼?

함수 내부에서 함수를 정의해야하는 가장 좋은 이유 무엇입니까 ?

당신이 실제로 원하는 것은 딩당 폐쇄 입니다.


1
이 답변은 @ Mayou36입니다.
CivFan

2
예, 감사합니다. 이것이 제가 찾던 답변입니다. 이것은 최선의 방법으로 질문에 대답합니다. 엄격하게 말하지는 않지만 이유를 명시하여 인라인 정의를 크게 권장하지 않습니다. 그것이 (pythonic :)) 답변이어야합니다!
Mayou36

10

실제로 하나의 함수를 다른 함수 안에 선언하는 것이 좋습니다. 데코레이터를 만드는 데 특히 유용합니다.

그러나 일반적으로 함수가 복잡한 경우 (10 줄 이상) 모듈 수준에서 선언하는 것이 좋습니다.


2
가능하지만 동의 할만한 충분한 이유가 필요합니다. 클래스 내에서만 사용되는 함수에 앞의 밑줄을 사용하는 것이 더 파이썬 일 것입니다.
chmullig

3
클래스 내에서 예, 그러나 함수 내에서만 어떻습니까? 캡슐화는 계층 적입니다.
Paul Draper

@PaulDraper "캡슐화는 계층 적입니다"-아니요! 누가 그렇게 말합니까? 캡슐화는 단순한 상속보다 훨씬 넓은 원칙입니다.
Mayou36

7

중첩 된 함수를 사용하는 경우 성능에 미치는 영향에 대한 질문을하고 싶기 때문에이 질문을 찾았습니다. 쿼드 코어 2.5GHz Intel i5-2530M 프로세서가 장착 된 Windows 노트북에서 Python 3.2.5를 사용하여 다음 기능에 대한 테스트를 실행했습니다.

def square0(x):
    return x*x

def square1(x):
    def dummy(y):
        return y*y
    return x*x

def square2(x):
    def dummy1(y):
        return y*y
    def dummy2(y):
        return y*y
    return x*x

def square5(x):
    def dummy1(y):
        return y*y
    def dummy2(y):
        return y*y
    def dummy3(y):
        return y*y
    def dummy4(y):
        return y*y
    def dummy5(y):
        return y*y
    return x*x

square1, square2 및 square5에 대해서도 다음 20 회 측정했습니다.

s=0
for i in range(10**6):
    s+=square0(i)

다음 결과를 얻었습니다

>>> 
m = mean, s = standard deviation, m0 = mean of first testcase
[m-3s,m+3s] is a 0.997 confidence interval if normal distributed

square? m     s       m/m0  [m-3s ,m+3s ]
square0 0.387 0.01515 1.000 [0.342,0.433]
square1 0.460 0.01422 1.188 [0.417,0.503]
square2 0.552 0.01803 1.425 [0.498,0.606]
square5 0.766 0.01654 1.979 [0.717,0.816]
>>> 

square0중첩 함수가없고, 중첩 함수 square1가 하나 있으며 , 중첩 함수 square2가 2 개 이며, 중첩 함수 square5가 5 개 있습니다. 중첩 함수는 선언되었지만 호출되지는 않습니다.

따라서 호출하지 않는 함수에 5 개의 중첩 함수를 정의한 경우 함수의 실행 시간은 중첩 함수가없는 함수의 두 배입니다. 중첩 함수를 사용할 때 조심해야한다고 생각합니다.

이 출력을 생성하는 전체 테스트의 Python 파일은 ideone 에서 찾을 수 있습니다 .


5
당신이하는 비교는 실제로 유용하지 않습니다. 함수에 더미 문을 추가하고 느리게 말하는 것과 같습니다. martineau의 예는 실제로 캡슐화 된 함수를 사용하며 그의 예제를 실행하여 성능 차이를 느끼지 못합니다.
kon psych

-1 질문을 다시 읽으십시오. 약간 느리다고 말할 수도 있지만, 주로 성능 때문이 아니라 일반적인 관행 때문에 그렇게하는 것이 좋은 생각인지 물었습니다.
Mayou36

@ Mayou36 죄송합니다. 3 년 후 답변이 게시되었습니다.
기적 173

안타깝게도 여전히 사용 가능한 답변이 없습니다.
Mayou36

4

노출 API에 대한 원칙 일뿐입니다.

파이썬을 사용하면 외부 공간 (모듈 또는 클래스)에서 노출 API를 피하는 것이 좋습니다. 함수는 좋은 캡슐화 장소입니다.

좋은 생각이 될 수 있습니다. 당신이 보장 할 때

  1. 내부 기능은 외부 기능 에서만 사용됩니다.
  2. 내부자 함수는 코드가 말하기 때문에 그 목적을 설명하기에 좋은 이름을 가지고 있습니다.
  3. 동료 또는 다른 코드 리더가 코드를 직접 이해할 수 없습니다.

그럼에도 불구하고,이 기술을 남용하면 문제가 발생할 수 있으며 설계 결함을 암시합니다.

내 특급에서 당신의 질문을 오해 할 수도 있습니다.


4

따라서 결국 파이썬 구현이 얼마나 똑똑한 지에 대한 질문입니다. 특히 내부 함수가 클로저가 아닌 단순히 함수에 필요한 도우미 만있는 경우 특히 그렇습니다.

깨끗하고 이해하기 쉬운 디자인은 필요한 곳에만 있고 다른 곳에 노출되지 않은 기능은 모듈, 메소드 클래스 또는 다른 함수 또는 메소드 내부에 포함되는지 여부에 관계없이 좋은 디자인입니다. 잘하면 코드의 선명도를 향상시킵니다.

그리고 내부 함수가 클로저 일 때 다른 곳에서 사용하기 위해 포함 함수에서 반환되지 않더라도 명확성에 도움이 될 수 있습니다.

따라서 나는 일반적으로 그것들을 사용한다고 말하지만 실제로 성능에 대해 염려 할 때 발생할 수있는 성능 히트에 대해 알고 실제로 제거하는 것이 가장 좋은 프로파일 링을 수행하는 경우에만 제거하십시오.

작성한 모든 파이썬 코드에서 "내부 함수 BAD"를 사용하는 것을 조기에 최적화하지 마십시오. 부디.


다른 답변에서 볼 수 있듯이 실제로 성능 저하는 없습니다.
Mayou36

1

그런 식으로하는 것이 좋습니다.하지만 클로저를 사용하거나 함수를 반환하지 않으면 모듈 수준에 넣을 것입니다. 두 번째 코드 예제에서 당신이 의미하는 것을 상상합니다.

...
some_data = method_b() # not some_data = method_b

그렇지 않으면 some_data가 함수가됩니다.

모듈 수준에서 사용하면 다른 함수가 method_b ()를 사용할 수 있으며 설명서에 Sphinx (및 autodoc)와 같은 것을 사용하는 경우 method_b도 문서화 할 수 있습니다.

객체로 표현할 수있는 작업을 수행하는 경우 클래스의 두 가지 메소드에 기능을 배치하는 것을 고려할 수도 있습니다. 이것이 당신이 찾고있는 전부라면 논리도 포함합니다.


1

다음과 같은 작업을 수행하십시오.

def some_function():
    return some_other_function()
def some_other_function():
    return 42 

당신이 some_function()그것을 실행 하면 다음 실행some_other_function() 되고 42를 반환합니다.

편집 : 나는 원래 당신이 다른 내부에서 함수를 정의해서는 안된다고 말했지만 때로는 이것을하는 것이 실용적이라고 지적되었습니다.


나는 위의 사람들이 그들의 답변에 노력한 것에 감사하지만, 당신의 직설적이고 요점에 있습니다. 좋은.
C0NFUS3D

1
왜? 왜 그렇게하지 말아야합니까? 캡슐화가 좋지 않습니까? 나는 어떤 주장도 빠졌다. -1
Mayou36

@ Mayou36 나는 내 의견을 쓸 때 캡슐화가 무엇인지 알지 못했고 실제로 무엇인지 알지 못했습니다. 방금 좋지 않다고 생각했습니다. 함수를 외부에서 정의하는 대신 다른 함수 내부에서 정의하는 것이 유리한 이유를 설명 할 수 있습니까?
mdlp0716

2
예, 저는 할수 있습니다. 캡슐화의 개념을 찾을 수 있지만 간단히 말하면 필요하지 않은 정보를 숨기고 사용자에게 알아야 할 정보 만 노출시킵니다. 즉, some_other_function을 외부에서 정의하면 실제로 첫 번째 함수에 밀접하게 바인딩 된 네임 스페이스에 무언가가 추가됩니다. 또는 변수 측면에서 생각하십시오. 왜 로컬 변수 대 전역 변수가 필요합니까? 가능하면 함수 내부의 모든 변수를 정의하는 것이이 함수 내에서만 사용되는 변수에 대해 전역 변수를 사용하는 것보다 낫습니다 . 결국에는 복잡성을 줄이는 것이 전부입니다.
Mayou36

0

전역 변수 정의를 피하기 위해 사용할 수 있습니다. 이것은 다른 디자인에 대한 대안을 제공합니다. 문제에 대한 해결책을 제시하는 3 가지 디자인.

A) 전역이없는 함수 사용

def calculate_salary(employee, list_with_all_employees):
    x = _calculate_tax(list_with_all_employees)

    # some other calculations done to x
    pass

    y = # something 

    return y

def _calculate_tax(list_with_all_employees):
    return 1.23456 # return something

B) 전역 함수 사용

_list_with_all_employees = None

def calculate_salary(employee, list_with_all_employees):

    global _list_with_all_employees
    _list_with_all_employees = list_with_all_employees

    x = _calculate_tax()

    # some other calculations done to x
    pass

    y = # something

    return y

def _calculate_tax():
    return 1.23456 # return something based on the _list_with_all_employees var

C) 다른 함수 내에서 함수 사용

def calculate_salary(employee, list_with_all_employees):

    def _calculate_tax():
        return 1.23456 # return something based on the list_with_a--Lemployees var

    x = _calculate_tax()

    # some other calculations done to x
    pass
    y = # something 

    return y

솔루션 C) 는 내부 함수에서 변수를 선언하지 않고도 외부 함수의 범위에서 변수를 사용할 수 있습니다. 상황에 따라 유용 할 수 있습니다.


0

함수 파이썬에서 함수

def Greater(a,b):
    if a>b:
        return a
    return b

def Greater_new(a,b,c,d):
    return Greater(Greater(a,b),Greater(c,d))

print("Greater Number is :-",Greater_new(212,33,11,999))
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.