문제

나는 클로저에 대해 많이 읽었고 그것을 이해하고 있다고 생각합니다. 하지만 나 자신과 다른 사람들을 위해 그림을 흐리게 하지 않고 누군가 클로저를 가능한 한 간결하고 명확하게 설명할 수 있기를 바랍니다.나는 그것을 어디에, 왜 사용하고 싶은지 이해하는 데 도움이 될 수 있는 간단한 설명을 찾고 있습니다.

도움이 되었습니까?

해결책

폐쇄에 대한 폐쇄

객체는 메소드가 첨부 된 데이터이며, 폐쇄는 데이터가 첨부 된 기능입니다.

def make_counter():
    i = 0
    def counter(): # counter() is a closure
        nonlocal i
        i += 1
        return i
    return counter

c1 = make_counter()
c2 = make_counter()

print (c1(), c1(), c2(), c2())
# -> 1 2 1 2

다른 팁

간단 해:잠재적으로 제어 흐름이 해당 범위를 떠난 후에 포함 범위에서 변수를 참조하는 함수입니다.마지막 부분은 매우 유용합니다.

>>> def makeConstantAdder(x):
...     constant = x
...     def adder(y):
...         return y + constant
...     return adder
... 
>>> f = makeConstantAdder(12)
>>> f(3)
15
>>> g = makeConstantAdder(4)
>>> g(3)
7

12와 4는 각각 f와 g 내부에서 "사라졌습니다". 이 기능으로 인해 f와 g가 적절하게 닫힙니다.

좋아요 이 대략적이고 간결한 정의:

더 이상 활성화되지 않은 환경을 참조할 수 있는 기능입니다.

나는 추가 할 것이다

클로저를 사용하면 변수를 함수에 바인딩할 수 있습니다. 매개변수로 전달하지 않고.

매개변수를 허용하는 데코레이터는 클로저에 일반적으로 사용됩니다.클로저는 이러한 종류의 "함수 팩토리"에 대한 일반적인 구현 메커니즘입니다.나는 자주 클로저를 사용하기로 선택합니다. 전략 패턴 런타임 시 데이터에 의해 전략이 수정되는 경우.

익명의 블록 정의를 허용하는 언어(예: Ruby, C#)에서는 클로저를 사용하여 새롭고 새로운 제어 구조를 구현할 수 있습니다.익명 블록이 없다는 점은 Python 클로저의 한계.

솔직히 말해서, "클로저"가 정확히 무엇인지, "클로저"가 무엇인지 명확히 알지 못했다는 점을 제외하면 나는 클로저를 완벽하게 이해합니다.용어 선택 뒤에 숨은 논리를 찾는 것을 포기하는 것이 좋습니다.

어쨌든, 내 설명은 다음과 같습니다.

def foo():
   x = 3
   def bar():
      print x
   x = 5
   return bar

bar = foo()
bar()   # print 5

여기서 핵심 아이디어는 'x'가 범위를 벗어났고 없어져야 함에도 불구하고 foo에서 반환된 함수 객체가 로컬 변수 'x'에 대한 후크를 유지한다는 것입니다.이 후크는 당시 var가 가지고 있던 값뿐만 아니라 var 자체에 대한 것이므로 bar가 호출되면 3이 아닌 5가 인쇄됩니다.

또한 Python 2.x에는 폐쇄가 제한되어 있다는 점을 분명히 하세요.'x = bla'라고 쓰면 foo의 'x'에 할당되는 것이 아니라 bar에 로컬 'x'가 선언되기 때문에 'bar' 안의 'x'를 수정할 수 있는 방법이 없습니다.이는 Python 할당=선언의 부작용입니다.이 문제를 해결하기 위해 Python 3.0에서는 nonlocal 키워드를 도입했습니다.

def foo():
   x = 3
   def bar():
      print x
   def ack():
      nonlocal x
      x = 7
   x = 5
   return (bar, ack)

bar, ack = foo()
ack()   # modify x of the call to foo
bar()   # print 7

나는 클로저가 무엇인지 설명하는 것과 동일한 맥락에서 트랜잭션이 사용된다는 것을 들어본 적이 없으며 여기에는 실제로 트랜잭션 의미가 없습니다.

외부 변수(상수)를 "닫기" 때문에 클로저라고 부릅니다. 즉, 이는 단순한 함수가 아니라 함수가 생성된 환경의 인클로저입니다.

다음 예에서 x를 변경한 후 클로저 g를 호출하면 g가 x 위에 닫히므로 g 내의 x 값도 변경됩니다.

x = 0

def f():
    def g(): 
        return x * 2
    return g


closure = f()
print(closure()) # 0
x = 2
print(closure()) # 4

다음은 클로저의 일반적인 사용 사례입니다. GUI 요소에 대한 콜백입니다(이것은 버튼 클래스를 하위 클래스화하는 대신 사용할 수 있습니다).예를 들어, 버튼 누름에 대한 응답으로 호출되는 함수를 구성하고 클릭 처리에 필요한 상위 범위의 관련 변수를 "닫을" 수 있습니다.이 방법을 사용하면 동일한 초기화 함수에서 매우 복잡한 인터페이스를 연결하여 모든 종속성을 클로저에 구축할 수 있습니다.

Python에서 클로저는 변수가 불변하게 바인딩된 함수의 인스턴스입니다.

실제로, 데이터 모델은 이것을 설명합니다 기능 설명에 ' __closure__ 기인하다:

없음 또는 셀 튜플 함수의 자유 변수에 대한 바인딩을 포함합니다.읽기 전용

이를 보여주기 위해:

def enclosure(foo):
    def closure(bar):
        print(foo, bar)
    return closure

closure_instance = enclosure('foo')

분명히, 우리는 이제 변수 이름에서 가리키는 함수가 있다는 것을 알고 있습니다. closure_instance.표면적으로는 객체와 함께 호출하면 bar, 문자열을 인쇄해야 합니다. 'foo' 그리고 문자열 표현이 무엇이든 간에 bar 이다.

실제로 문자열 'foo' ~이다 함수의 인스턴스에 바인딩되어 있으며 여기서 액세스하여 직접 읽을 수 있습니다. cell_contents 튜플의 첫 번째(그리고 유일한) 셀의 속성 __closure__ 기인하다:

>>> closure_instance.__closure__[0].cell_contents
'foo'

여담으로, 셀 객체는 C API 문서에 설명되어 있습니다:

"셀"객체는 여러 범위에서 참조 된 변수를 구현하는 데 사용됩니다.

그리고 우리는 클로저의 사용법을 보여줄 수 있습니다. 'foo' 기능에 갇혀 있고 변경되지 않습니다.

>>> closure_instance('bar')
foo bar
>>> closure_instance('baz')
foo baz
>>> closure_instance('quux')
foo quux

그리고 아무것도 그것을 바꿀 수 없습니다:

>>> closure_instance.__closure__ = None
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: readonly attribute

부분 기능

주어진 예에서는 클로저를 부분 함수로 사용하지만 이것이 우리의 유일한 목표라면 동일한 목표를 다음과 같이 달성할 수 있습니다. functools.partial

>>> from __future__ import print_function # use this if you're in Python 2.
>>> partial_function = functools.partial(print, 'foo')
>>> partial_function('bar')
foo bar
>>> partial_function('baz')
foo baz
>>> partial_function('quux')
foo quux

부분 함수 예제에 맞지 않는 더 복잡한 클로저도 있으며, 시간이 허락하는 대로 더 자세히 설명하겠습니다.

다음은 Python3 클로저의 예입니다.

def closure(x):
    def counter():
        nonlocal x
        x += 1
        return x
    return counter;

counter1 = closure(100);
counter2 = closure(200);

print("i from closure 1 " + str(counter1()))
print("i from closure 1 " + str(counter1()))
print("i from closure 2 " + str(counter2()))
print("i from closure 1 " + str(counter1()))
print("i from closure 1 " + str(counter1()))
print("i from closure 1 " + str(counter1()))
print("i from closure 2 " + str(counter2()))

# result

i from closure 1 101
i from closure 1 102
i from closure 2 201
i from closure 1 103
i from closure 1 104
i from closure 1 105
i from closure 2 202
# A Closure is a function object that remembers values in enclosing scopes even if they are not present in memory.

# Defining a closure

# This is an outer function.
def outer_function(message):
    # This is an inner nested function.
    def inner_function():
        print(message)
    return inner_function

# Now lets call the outer function and return value bound to name 'temp'
temp = outer_function("Hello")
# On calling temp, 'message' will be still be remembered although we had finished executing outer_function()
temp()
# Technique by which some data('message') that remembers values in enclosing scopes 
# even if they are not present in memory is called closures

# Output: Hello

클로저가 충족해야 하는 기준은 다음과 같습니다.

  1. 중첩된 함수가 있어야 합니다.
  2. 중첩 함수는 바깥쪽 함수에 정의된 값을 참조해야 합니다.
  3. 바깥쪽 함수는 중첩된 함수를 반환해야 합니다.

# Example 2
def make_multiplier_of(n): # Outer function
    def multiplier(x): # Inner nested function
        return x * n
    return multiplier
# Multiplier of 3
times3 = make_multiplier_of(3)
# Multiplier of 5
times5 = make_multiplier_of(5)
print(times5(3)) # 15
print(times3(2)) #  6

나에게 있어 "클로저"는 자신이 생성된 환경을 기억할 수 있는 기능입니다.이 기능을 사용하면 클로저 내에서 변수나 메소드를 사용할 수 있습니다. 즉, 더 이상 존재하지 않거나 범위로 인해 도달할 수 없기 때문에 사용할 수 없습니다.Ruby에서 다음 코드를 살펴보겠습니다.

def makefunction (x)
  def multiply (a,b)
    puts a*b
  end
  return lambda {|n| multiply(n,x)} # => returning a closure
end

func = makefunction(2) # => we capture the closure
func.call(6)    # => Result equal "12"  

"곱하기" 방법과 "x" 변수가 모두 더 이상 존재하지 않는 경우에도 작동합니다.모두 기억해야 할 폐쇄 기능 때문입니다.

우리 모두는 사용했습니다 데코레이터 파이썬에서.파이썬에서 클로저 함수가 무엇인지 보여주는 좋은 예입니다.

class Test():
    def decorator(func):
        def wrapper(*args):
            b = args[1] + 5
            return func(b)
        return wrapper

@decorator
def foo(val):
    print val + 2

obj = Test()
obj.foo(5)

여기서 최종 값은 12입니다.

여기서 래퍼 함수는 "어휘적 클로저"이기 때문에 func 객체에 액세스할 수 있으며 상위 속성에 액세스할 수 있습니다.그렇기 때문에 func 객체에 접근할 수 있습니다.

클로저에 대한 예시와 설명을 공유하고 싶습니다.나는 Python 예제와 스택 상태를 보여주는 두 개의 그림을 만들었습니다.

def maker(a, b, n):
    margin_top = 2
    padding = 4
    def message(msg):
        print('\n’ * margin_top, a * n, 
            ' ‘ * padding, msg, ' ‘ * padding, b * n)
    return message

f = maker('*', '#', 5)
g = maker('', '♥’, 3)
…
f('hello')
g(‘good bye!')

이 코드의 출력은 다음과 같습니다.

*****      hello      #####

      good bye!    ♥♥♥

다음은 함수 개체에 연결된 스택과 클로저를 보여주는 두 그림입니다.

메이커로부터 기능이 반환된 경우

나중에 함수가 호출될 때

매개변수나 비지역 변수를 통해 함수가 호출되는 경우 코드에는 margin_top, padding은 물론 a, b, n과 같은 지역 변수 바인딩이 필요합니다.함수 코드가 제대로 작동하려면 오래 전에 사라진 메이커 함수의 스택 프레임에 접근할 수 있어야 하며, 이는 '메시지'의 함수 개체와 함께 찾을 수 있는 클로저에 백업되어 있습니다.

내가 본 클로저에 대한 가장 좋은 설명은 메커니즘을 설명하는 것이었습니다.다음과 같이 진행되었습니다.

프로그램 스택을 각 노드에 자식이 하나만 있고 단일 리프 노드가 현재 실행 중인 프로시저의 컨텍스트인 퇴화된 트리라고 상상해 보세요.

이제 각 노드가 하나의 자식만 가질 수 있다는 제약 조건을 완화합니다.

이렇게 하면 로컬 컨텍스트(예:돌아올 때 스택에서 팝되지 않습니다.)다음에 프로시저가 호출되면 호출은 이전 스택(트리) 프레임을 선택하고 중단된 부분부터 계속 실행됩니다.

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