나는 폐쇄에 대해 많이 읽고 이해한다고 생각하지만 나 자신과 다른 사람들을 위해 그림을 흐리게하지 않고 누군가가 폐쇄를 가능한 한 간결하고 명확하게 설명 할 수 있기를 바랍니다. 나는 어디서 그리고 왜 사용하고 싶은지 이해하는 데 도움이 될 간단한 설명을 찾고 있습니다.
답변:
객체는 메서드가 연결된 데이터이고 클로저는 데이터가 연결된 함수입니다.
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
nonlocal
, 변경 가능한 객체 등을 사용하여 파이썬 2 키워드 L = [0] \n def counter(): L[0] += 1; return L[0]
(이 경우 바인딩이 다른 개체에 대한) 즉, 당신은 이름을 변경할 수 없습니다 하지만 당신은 이름이 의미하는 가변 객체 자체를 변경할 수 있습니다 에. Python에서는 정수가 불변이므로 목록이 필요합니다.
간단합니다. 제어 흐름이 해당 범위를 떠난 후 잠재적으로 포함 범위에서 변수를 참조하는 함수입니다. 마지막 부분은 매우 유용합니다.
>>> 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가 적절한 클로저를 만드는 이유입니다.
constant = x
. return y + x
중첩 된 함수에서 수행 할 수 있으며 (또는 이름으로 인수를받을 수 있습니다. constant
) 제대로 작동합니다. 인수는 비 인수 로컬과 다르지 않게 클로저에 의해 캡처됩니다.
이 거칠고 간결한 정의가 마음 에 듭니다 .
더 이상 활성화되지 않은 환경을 참조 할 수있는 기능입니다.
나는 추가 할 것이다
클로저를 사용하면 변수를 매개 변수로 전달하지 않고도 함수에 변수를 바인딩 할 수 있습니다 .
매개 변수를 허용하는 데코레이터는 클로저에 일반적으로 사용됩니다. 클로저는 이러한 종류의 "기능 팩토리"에 대한 일반적인 구현 메커니즘입니다. 나는 전략이 런타임에 데이터에 의해 수정 될 때 전략 패턴 에서 클로저를 자주 사용 합니다.
익명의 블록 정의를 허용하는 언어 (예 : Ruby, C #)에서 클로저를 사용하여 새로운 새로운 제어 구조를 구현할 수 있습니다. 익명 블록의 부족은 Python에서 클로저의 한계 중 하나 입니다.
솔직히 말해서, "폐쇄"가 정확히 무엇인지, 그리고 그것에 대해 "폐쇄"가 무엇인지에 대해 명확하지 않은 것을 제외하고는 클로저를 완벽하게 이해합니다. 용어 선택의 논리를 찾는 것을 포기하는 것이 좋습니다.
어쨌든, 여기에 내 설명이 있습니다.
def foo():
x = 3
def bar():
print x
x = 5
return bar
bar = foo()
bar() # print 5
여기서 핵심 아이디어는 foo에서 반환 된 함수 객체가 'x'가 범위를 벗어 났고 없어야하지만 로컬 var 'x'에 대한 후크를 유지한다는 것입니다. 이 후크는 var 자체에 대한 것이지 var가 당시에 가졌던 값이 아니므로 bar가 호출되면 3이 아닌 5를 인쇄합니다.
또한 Python 2.x에는 제한된 클로저가 있음을 분명히하십시오. 'x = bla'를 작성하면 foo의 'x'에 할당되지 않고 bar에서 로컬 'x'를 선언하기 때문에 'bar'내부에서 'x'를 수정할 수있는 방법이 없습니다. . 이것은 Python의 assignment = declaration의 부작용입니다. 이 문제를 해결하기 위해 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
g()
계산 x * 2
하지만 아무것도 반환하지 않습니다. 이어야합니다 return x * 2
. 그럼에도 불구하고 "폐쇄"라는 단어에 대한 설명은 +1.
다음은 클로저에 대한 일반적인 사용 사례입니다-GUI 요소에 대한 콜백 (버튼 클래스를 서브 클래 싱하는 대신). 예를 들어, 버튼 누름에 대한 응답으로 호출되는 함수를 생성하고 클릭 처리에 필요한 상위 범위의 관련 변수를 "닫기"할 수 있습니다. 이렇게하면 동일한 초기화 함수에서 매우 복잡한 인터페이스를 연결하여 모든 종속성을 클로저에 구축 할 수 있습니다.
파이썬에서 클로저는 변수가 불변으로 바인딩 된 함수의 인스턴스입니다.
실제로 데이터 모델 은 함수의 __closure__
속성 에 대한 설명에서이를 설명 합니다.
함수의 자유 변수에 대한 바인딩을 포함 하는 셀 의 튜플 또는 없음 . 읽기 전용
이를 증명하려면 :
def enclosure(foo):
def closure(bar):
print(foo, bar)
return closure
closure_instance = enclosure('foo')
분명히 우리는 이제 변수 name에서 가리키는 함수가 있다는 것을 알고 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
부분 함수 예제에 맞지 않는 더 복잡한 클로저도 있습니다. 시간이 허락하는 한 더 자세히 설명하겠습니다.
# 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
폐쇄 기준은 다음과 같습니다.
# 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
다음은 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
저에게 "클로저"는 생성 된 환경을 기억할 수있는 기능입니다. 이 기능을 사용하면 클로저 내에서 변수 나 메서드를 사용할 수 있습니다. 다른 방법으로 더 이상 존재하지 않거나 범위로 인해 사용할 수 없기 때문에 사용할 수 없습니다. 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"
"multiply"메소드와 "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 객체에 접근 할 수 있습니다.
클로저에 대한 예제와 설명을 공유하고 싶습니다. 파이썬 예제와 스택 상태를 보여주는 두 개의 그림을 만들었습니다.
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과 같은 지역 변수 바인딩이 필요합니다. 함수 코드가 작동하는지 확인하려면 오래 전에 사라진 메이커 함수의 스택 프레임에 액세스 할 수 있어야합니다.이 스택 프레임은 '메시지의 함수 객체와 함께 찾을 수있는 클로저에 백업됩니다.
내가 본 종결에 대한 가장 좋은 설명은 메커니즘을 설명하는 것입니다. 다음과 같이 진행되었습니다.
프로그램 스택을 각 노드에 자식이 하나만 있고 단일 리프 노드가 현재 실행중인 프로 시저의 컨텍스트 인 퇴화 트리라고 상상해보십시오.
이제 각 노드가 하나의 자식 만 가질 수 있다는 제약을 완화합니다.
이렇게하면 로컬 컨텍스트를 버리지 않고 프로 시저에서 반환 할 수있는 구성 ( 'yield')을 가질 수 있습니다 (즉, 반환 할 때 스택에서 팝하지 않음). 다음에 프로 시저가 호출 될 때 호출은 이전 스택 (트리) 프레임을 선택하고 중단 된 위치에서 계속 실행합니다.
nonlocal
파이썬 3에서 추가되었습니다, 파이썬 2.X는하지 않았다 풀에, 읽기 - 쓰기 폐쇄 (즉 당신이 변수 닫혀 있지만, 그 값을 변경하지 읽을 수)