폐쇄
인터넷에는 Python 클로저를 소개하는 기사가 많이 있습니다. 이 기사에서는 요구 사항 문제를 해결하여 클로저에 대해 알아봅니다.
이 요구 사항은 다음과 같습니다. 우리는 공부 시간을 분 단위로 계속 기록해야 합니다. 마치 2분 공부하면 2가 돌아오고, 좀 있다가 10분 공부하고 12가 돌아오는 셈이다. 학습시간은 이렇게 쌓인다.
이러한 요구에 직면하여 우리는 일반적으로 시간을 기록하는 전역 변수를 만든 다음 각 학습 시간을 추가하는 방법을 사용합니다. 일반적으로 다음과 같은 형식으로 작성됩니다.
time = 0 def insert_time(min): time = time + min return time print(insert_time(2)) print(insert_time(10))
잘 생각해 보세요. 무슨 문제야?
실제로 이렇게 하면 Python에서 오류가 보고됩니다. 다음과 같은 오류가 보고됩니다:
UnboundLocalError: local variable 'time' referenced before assignment
그 이유는 Python에서 함수가 전역 변수와 동일한 이름을 사용하고 변수의 값을 변경하면 변수가 지역 변수가 되기 때문입니다. 정의하지 않고 함수를 실행하므로 이 오류가 보고됩니다.
정말로 전역 변수를 참조하고 함수에서 수정하려면 어떻게 해야 하나요?
global 키워드를 사용할 수 있으며 구체적인 수정 사항은 다음과 같습니다.
time = 0 def insert_time(min): global time time = time + min return time print(insert_time(2)) print(insert_time(10))
출력 결과는 다음과 같습니다.
2 12
단, 여기서는 전역 변수를 사용하여 개발 중에는 최대한 전역 변수를 사용하지 않는 것이 좋습니다. . 다양한 모듈과 함수가 전역 변수에 자유롭게 액세스할 수 있으므로 전역 변수는 예측할 수 없게 될 수 있습니다. 예를 들어 프로그래머 A가 전역 변수 time의 값을 수정하고, 프로그래머 B도 time을 수정하면 오류가 있으면 찾아서 수정하기가 어렵습니다.
전역 변수는 함수나 모듈 간의 다양성을 감소시킵니다. 다양한 기능이나 모듈은 전역 변수에 따라 달라집니다. 마찬가지로, 전역 변수는 코드의 가독성을 감소시키며, 독자는 호출되는 특정 변수가 전역 변수라는 것을 알지 못할 수 있습니다.
더 좋은 방법이 있나요?
이번에는 문제를 해결하기 위해 클로저를 사용합니다. 먼저 코드를 직접 살펴보겠습니다.
time = 0 def study_time(time): def insert_time(min): nonlocal time time = time + min return time return insert_time f = study_time(time) print(f(2)) print(time) print(f(10)) print(time)
출력 결과는 다음과 같습니다.
2 0 12 0
여기서 가장 직접적인 징후는 전역 변수 time이 수정되지 않았다는 것입니다. 지금까지는 함수나 다른 범위에서 외부(비전역) 변수의 사용을 나타내는 비로컬 키가 여기에서 계속 사용됩니다. 그렇다면 위 코드의 구체적인 실행 프로세스는 무엇입니까? 아래 그림을 보면 알 수 있습니다.
외부 함수의 로컬 범위에 있는 변수에 내부 함수의 로컬 범위에서 액세스할 수 있는 이러한 동작을 클로저라고 합니다. 이를 보다 직접적으로 표현하는 방법은 함수가 객체로 반환될 때 외부 변수가 포함되어 클로저를 형성하는 것입니다. k
클로저는 전역 변수의 사용을 방지합니다. 또한 클로저는 함수가 작동하는 일부 데이터(환경)와 연결될 수 있도록 합니다. 그리고 클로저를 사용하면 코드를 더욱 우아하게 만들 수 있습니다. 그리고 다음 글에서 언급할 데코레이터 역시 클로저를 기반으로 구현되었습니다.
이쯤 되면 종결인가, 종결인가 하는 의문이 들텐데요. 이 함수가 클로저인지 확인할 수 있는 방법이 있나요?
예, 모든 함수에는 __closure__ 속성이 있습니다. 함수가 클로저인 경우 셀로 구성된 튜플 객체를 반환합니다. 셀 객체의 cell_contents 속성은 클로저에 저장된 변수입니다.
인쇄해서 경험해 봅시다:
time = 0 def study_time(time): def insert_time(min): nonlocal time time = time + min return time return insert_time f = study_time(time) print(f.__closure__) print(f(2)) print(time) print(f.__closure__[0].cell_contents) print(f(10)) print(time) print(f.__closure__[0].cell_contents)
인쇄된 결과는
(<cell at 0x0000000000410C48: int object at 0x000000001D6AB420>,) 2 0 2 12 0 12
인쇄 결과를 보면 전달된 값이 항상 클로저의 cell_contents에 저장되어 있음을 알 수 있습니다. 따라서 이것이 가장 큽니다. 클로저의 기능은 상위 함수의 변수가 그 안에 정의된 함수에 바인딩됩니다. 클로저를 생성한 상위 함수가 해제되더라도 클로저는 여전히 존재합니다.
클로저 프로세스는 실제로 클래스(부모 함수)가 인스턴스(클로저)를 생성하는 것과 같습니다. 차이점은 부모 함수가 호출될 때만 실행되고 해당 환경은 실행 후에 해제되는 반면 클래스는 생성될 때 생성됩니다. 일반적으로 범위는 프로그램이 실행된 후에 해제됩니다. 따라서 재사용이 필요하고 클래스의 동작으로 정의하기에는 충분하지 않은 일부 함수의 경우 클로저를 사용하면 클래스를 사용하는 것보다 적은 리소스를 차지합니다. 더 가볍고 유연합니다.