2017년 9월 21일 목요일

Python decorator와 wrapt 에 관하여

https://codewithoutrules.com/2017/08/10/python-decorators/?utm_source=newsletter_mailer&utm_medium=email&utm_campaign=weekly

를 의역(?)을 간단히 했습니다. 아직 다 정리한 것은 아니고 또 잘못된 내용이 있을 수 있으니 원문을 참고해주세요 :)

-----
Python decorator는 결점이 있지만 매우 유용합니다.

코드를 쓰기 쉽고 좀 더 읽기 쉽게 만들지만 ‘데코레이티드 된 코드를 호출하는 프로그래머’가 use case를 만드는 것을 방해합니다.

파이썬 프로그래머라면 이 포스트를 통해 왜 decorator가 있는지, 그리고 그것의 한계를 어떻게 상쇄하는지 배울 수 있습니다.

그리고 파이썬 프로그래머가 아니라도, 다른 코드에서 이하의 명심할 내용들이 되었으면 합니다.

  1. 왜 Decorator가 존재하는가: 코드를 만들고(authoring) 읽기
프로그래밍 언어는 다음의 4가지 청중을 만족해야 합니다.
  • 소스코드가 돌아가는 컴퓨터
  • 소스코드를 작성한 프로그래머(The author)
  • 소스코드를 미래에 읽을 사람(A future reader)
  • 자신의 함수나 클래스스의 코드를 쓰면서 프로그래머 이를 호출할 프로그래머(A future caller)
Python decorator는 이중 Auther와 readers를 위해 생성되었으나, Caller에 대해서는 그렇지 않습니다.
Decorator가 하는 것과 어떻게 이것이 author와 가독성에 좋은지 알아봅시다.
Java의 synchronized keyword를 Emulate한다고 생각해 봅시다. 당신은 Class의 Method가 Lock이 걸려서 한번에 한개의 Thread만 Call하여 실행하는 것을 원합니다.
아래의 코드를 따라서 synchronized 함수가 새로 어떻게 만들고 주어진 것을 감싸서 교체하는지 확인합시다.
{코드}
이 코드를 통해 synchronized는 다음과 같이 사용될 수 있습니다.
{코드}
Author에게는 이는 문제가 많은 사용법입니다. “Add”를 두번 써야 하기에 오타가 생길 가능 수 있습니다.
Reader는 Add()가 시작이 아닌 끝에 synchronized 된다고 알게됩니다.
Python은 그러므로 Decorator syntax를 제공하고, 이를 통해 아래와 같이 더 간결하게 만들 수 있습니다.
{코드}
  1. Decorators가 실패하는 시점: 코드를 부를때
Decorator의 문제는 Decorated Function(Decorator가 적용된 함수)의 호출을 원하는 Programmers의 요구를 만족시키지 못하는 것 입니다.
ExampleSynchronizedClass 의 User에게 당신은 Editor 나 IDE가 add의 docstring을 보이고, 적절한 signature를 발견하는 것을 원합니다.(?)
당신이 작성한 Documentation이나 코드로 부터 자동으로 생성된 API reference가 있으면 더 그렇죠.(?)
그런데 사실, signature, name dockstring과 같은 것을 얻는 것은 Wrapper function을 위함입니다.
{코드}
이것을 해결하기 위해 Python은 functools.wraps 라는 utility decorator를 제공하는데요, 이것은 wrapped fucntion의 name이나 docstring같은 attributes를 복사합니다.
Decorator의 구현을 변경해봅시다.
{코드}
그러면 우리는 더 좋은 help를 얻을 수 있죠.
{코드}
3.4 이전 버전의 Python의 signature는 아직 잘못되었습니다. 이것은 여전히 감싸진 함수가 아닌 Wrapper의 signature를 나타내죠.
만약 예전 버전의 Python을 지원하려고 한다면, 하나의 방법은 wrapt라는 3rd party library를 사용하는 것입니다.
functools.wraps대신 Wrapt 를 사용해서 Decorator를 다시 정의 해보죠.
{코드}
이를 사용해서 하위 버전 지원 뿐 아니라 약간의 간결함도 얻을 수 있습니다.
  1. 모든 Audiences 다루기
Functors.wraps와 wrapt의 trick을 쓰는 동안, 그들은 여전히 당신이 당신이 새 Decorator를 정의하는 모든 순간마다 그들을 사용하는 것을 기억하기를 원합니다.
아마 틀림없이 이는 Python 의 실수(?) 입니다. Library code가 이를 고치는 것에 의존하는 것 보다 더 좋은 것은 @ syntax를 python 언어 자신이 제공하는 것이죠.
당신이 library를 만들때, 혹은 심지어 programming language 를 디자인 할 때, 4가지의 audiences: The computer, authors, readers and callers를 지원하는 것은 언제나 가치있는 일입니다.
그리고 Decorator를 만드는 Python Programmer라면, wrapt를 사용하세요. 이는 caller와 reader를 더 행복하게 해줄 겁니다.
-----
뭔가 결론이 좀 이상하지만.. 여튼 도움이 되네요.