CodeOnWeb
로그인

부록 B. 특수 함수 이름

특수 함수에 대해서 알아봅니다.

Park Jonghyun 2015/09/01, 23:58

내용

파이썬 3에 뛰어들기 (22)

챕터 -1. “파이썬 3로 뛰어들기”에서 달라진 점 챕터 0. 파이썬 설치하기 챕터 1. 첫 파이썬 프로그램 챕터 2. 고유 자료형 챕터 3. 컴프리헨션(Comprehensions) 챕터 4. 문자열(Strings) 챕터 5. 정규표현식(Regular Expressions) 챕터 6. 클로저와 제너레이터(Closures & Generators) 챕터 7. 클래스와 반복자(Classes & Iterators) 챕터 8. 고급 반복자(Advanced Iterators) 챕터 9. 단위 테스트(Unit Testing) 챕터 10. 리팩토링(Refactoring) 챕터 11. 파일 챕터 12. XML 챕터 13. 파이썬 객체 직렬화 챕터 14. HTTP 웹서비스 챕터 15. 사례 연구: chardet을 파이썬 3로 이식하기 챕터 16. 파이썬 라이브러리 패키징하기 부록 A. 2to3를 이용해서 코드를 파이썬 3로 이식하기 부록 B. 특수 함수 이름 부록 C. 이제 어디로 가야 할까요? 부록 D. 문제 해결

"제 특기는 다른 사람들이 틀릴 때 저는 맞는 겁니다."
- 조지 버나드 쇼(George Bernard Shaw)

뛰어들기

이 책 전반에 걸쳐 여러분은 "특수 메소드"의 예를 보았습니다. 특정한 문법을 사용하면 파이썬이 어떤 "마법"을 부리는 그런 메소드지요. 특수 메소드를 사용하면 여러분의 클래스는 집합이나 사전, 함수, 심지어 숫자와 같이 동작할 수 있습니다. 이 부록에서는 우리가 이미 봤던 특수 메소드에 대한 참조를 제공하고, 좀 더 난해한 메소드에 대한 간략한 소개를 목적으로 하고 있습니다.

기본

클래스에 대한 소개를 읽으셨다면 가장 흔히 사용되는 특수 메소드를 이미 보셨을겁니다. __init__() 메소드 말이지요. 제가 작성하는 클래스의 대부분은 결국 어떤 방식으로든 초기화가 필요했습니다. 이외에도 여러분이 작성한 클래스를 디버깅하는 데 특히 유용한 몇 가지 기본적인 특수 메소드가 있습니다.

Notes You Want… So You Write… And Python Calls…
#1 개체를 초기화 x = MyClass() x.__init__()
#2 문자열의 "공식적인" 표현 repr(x) x.__repr__()
#3 문자열의 "비공식적인" 값 str(x) x.__str__()
#4 바이트 배열의 "비공식적인" 값 bytes(x) x.__bytes__()
#5 서식화된 문자열 값 format(x, format_spec) x.__format__(format_spec)
  1. __init__() 메소드는 개체가 생성된 후에 호출됩니다. 실제 생성하는 과정을 제어하고 싶다면 __new__() 메소드를 사용할 수 있습니다.
  2. 관습적으로, __repr__() 메소드는 유효한 파이썬 표현식인 문자열을 반환해야 합니다.
  3. __str__() 메소드는 print(x)를 사용할 때 호출됩니다.
  4. 바이트 형은 파이썬 3에만 있으므로 __bytes__()도 파이썬 3에만 있습니다.
  5. 관습적으로 format_spec은 서식 명세를 위한 작은 언어에 부합해야 합니다. 파이썬 표준 라이브러리의 decimal.py은 자신의 __format__() 메소드를 제공합니다.

반복자처럼 작동하는 클래스

반복자 챕터에서 __iter__()와 __next__() 메소드를 이용해서 반복자를 어떻게 만드는지 살펴보았습니다.

Notes You Want… So You Write… And Python Calls…
#1 to iterate through a sequence iter(seq) seq.__iter__()
#2 to get the next value from an iterator next(seq) seq.__next__()
#3 to create an iterator in reverse order reversed(seq) seq.__reversed__()
  1. __iter__() 메소드는 새로운 반복자를 만들 때마다 호출됩니다. 초기값을 이용해 반복자를 초기화하기 좋은 장소입니다.
  2. __next__() 메소드는 반복자에서 다음 값을 받아오려고 할 때 호출됩니다.
  3. __reversed__() 메소드는 자주 사용되지 않습니다. 이미 존재하는 서열(sequence)을 받아 항목을 역순(마지막에서 처음으로)으로 yield하는 반복자를 반환합니다.

반복자 챕터에서 보았듯이, for 문은 반복자를 처리할 수 있습니다. 아래 순환문의 경우

파이썬 3는 반복자를 생성하기 위해 seq.__iter__()를 호출하고, 그 반복자의 각 x 값을 얻기 위해 __next__() 메소드를 호출합니다. __next__() 메소드가 StopIteration 예외를 발생시키면 for 문은 조용히 끝납니다.

계산된 속성(Computed Attributes)

Notes You Want… So You Write… And Python Calls…
#1 to get a computed attribute (unconditionally) x.my_property x.__getattribute__('my_property')
#2 to get a computed attribute (fallback) x.my_property x.__getattr__('my_property')
#3 to set an attribute x.my_property = value x.__setattr__('my_property',value)
#4 to delete an attribute del x.my_property x.__delattr__('my_property')
#5 to list all attributes and methods dir(x) x.__dir__()
  1. 여러분의 클래스에 __getattribute__() 메소드가 정의되어 있다면, 파이썬은 어떤 속성이나 메소드 이름에 대한 참조가 일어날 때마다(기분 나쁜 무한 루프에 빠질 수 있으므로 특수 메소드 이름은 예외입니다) 이 메소드를 호출합니다.
  2. 여러분의 클래스에 __getattr__() 메소드가 정의되어 있다면, 파이썬은 정상적인 모든 위치에서 속성을 찾아본 다음에야 이 메소드를 호출합니다. 예를 들어, 개체 x가 color라는 속성을 가지고 있을 경우, x.color를 참조하면 x.__getattr__('color')를 호출하기 전에 이미 정의된 x.color를 그냥 반환합니다.
  3. __setattr__() 메소드는 속성에 값을 할당할 때마다 호출됩니다.
  4. __delattr__() 메소드는 속성을 지울 때마다 호출됩니다.
  5. __dir__() 메소드는 __getattr__()이나 __getattribute__() 메소드를 정의했다면 유용하게 쓸 수 있습니다. 일반적으로 dir(x)를 호출하면 일반 속성과 메소드를 출력합니다. 만약 __getattr__() 메소드가 color 속성을 동적으로 다룬다면, dir(x)는 color를 접근 가능한 속성으로 출력하지 않습니다. __dir__() 메소드를 재정의하면 color를 접근 가능한 속성으로 등록할 수 있고, 이는 여러분의 클래스를 사용하고자 하는 다른 사람들이 클래스 내부를 들여다 보며 고생하지 않도록 도와줍니다.

__getattr__()과 __getattribute__() 메소드의 차이는 사소한 것 같지만 중요합니다. 두 가지 예제를 들어 설명해보겠습니다.

  1. 속성 이름이 __getattr__() 메소드 문자열로 전달됩니다. 만약 이름이 'color'라면 메소드는 값을 반환합니다. (이 예제에서는 임의대로 쓰여진 'PapayaWhip'을 반환하지만, 일반적으로 어떤 값으로 계산을 수행한 뒤에 그 결과를 반환합니다.)
  2. 해당하는 속성 이름이 없다면, __getattr__() 메소드는 AttributeError 예외를 발생시켜야 합니다. 그렇게 하지 않을 경우, 여러분의 코드는 존재하지 않는 속성에 접근할 때 아무 오류를 내지 않고 조용히 실패할겁니다. (기술적으로 보자면, 메소드가 예외를 발생시키거나 명시적으로 값을 반환하지 않을 경우 파이썬이 자동으로 None을 반환합니다. 다시 말해 명시적으로 정의되지 않은 속성은 None이 된다는 것이고, 이는 아마 원하는 바가 아닐겁니다.)
  3. dyn 개체는 color라는 이름의 속성을 가지고 있지 않으므로, __getattr__() 메소드가 호출되어 계산된 값이 반환됩니다.
  4. 명시적으로 dyn.color를 지정한 뒤에 dyn.color 값에 접근하면 __getattr__() 메소드가 호출되지 않습니다. dyn.color가 이미 개체에 정의되어 있기 때문입니다.

반면, __getattribute__() 메소드는 절대적이고 조건없이 실행됩니다.

  1. dyn.color 값을 제공하기 위해 __getattribute__() 메소드가 호출되었습니다.
  2. dyn.color를 명시적으로 설정하고 난 뒤에도 dyn.color에 접근하면 __getattribute__() 메소드가 호출됩니다. __getattribute__() 메소드가 존재하는 경우, 그 클래스의 모든 속성과 메소드에 접근할 때 __getattribute__()가 무조건 호출됩니다. 개체를 생성하고 나서 속성을 명시적으로 지정해주더라도 마찬가지입니다.

여러분이 만든 클래스에 __getattribute__() 메소드를 정의했다면, __setattr__() 메소드도 정의해서 속성값을 추적할 수 있게 하는 편이 좋습니다. 그렇지 않을 경우, 개체를 생성한 뒤에 설정한 모든 속성은 블랙홀로 빨려 들어가 다시 볼 수 없을겁니다.

__getattribute__() 메소드를 사용할 때에는 아주 조심해야 합니다. 파이썬이 여러분 클래스의 메소드 이름을 접근하려 할 때에도 호출되어버리니까요.

  1. 이 클래스는 언제나 AttributeError 예외를 일으키는 __getattribute__() 메소드를 정의하고 있습니다. 이러면 어떤 속성과 메소드에도 접근할 수 없습니다.
  2. hero.swim()을 호출하면 파이썬은 Rastan 클래스의 swim() 메소드를 찾습니다. 모든 속성과 메소드 접근은 __getattribute__()로 흘러가게 되므로, __getattribute__() 메소드가 실행되게 되겠군요. 그러면 __getattribute__() 메소드는 언제나 AttributeError 예외를 일으키므로 우리가 원하는 메소드를 찾을 수 없게 됩니다.

함수처럼 동작하는 클래스

__call__() 메소드를 정의함으로써 클래스 개체를 마치 함수를 호출하는 것처럼 호출 가능하게 만들 수도 있습니다.

Notes You Want… So You Write… And Python Calls…
to “call” an instance like a function my_instance() my_instance.__call__()

zipfile 모듈은 이 방법을 이용해서, 주어진 암호로 zip 파일을 암호화 하거나 암호를 해독하는 클래스를 정의합니다. zip 암호 해독 알고리듬을 사용하려면 암호 해독 과정의 상태를 저장해야 합니다. 암호 해독 클래스를 정의함으로써 이 상태를 각 클래스 개체 내부에 저장할 수 있습니다. 상태는 __init__() 메소드에서 초기화되고, 파일의 암호를 해독하는 과정에 업데이트 됩니다. 하지만 이 클래스는 함수처럼 "호출 가능"하므로, 개체를 map() 함수의 첫 번째 인자로 넘겨줄 수도 있습니다.

  1. _ZipDecryptor 클래스는 세 개의 순환키(rotating key)의 형태로 상태를 관리합니다. 이들은 이후에 _UpdateKeys() 메소드(윗 코드에 나오지는 않았습니다)에서 업데이트 됩니다.
  2. 클래스는 __call__() 메소드를 정의해서 클래서 개체가 함수처럼 호출 가능하게 만듭니다. 이 경우 __call__() 메소드는 zip 파일의 한 바이트를 해독하고, 해독된 바이트를 기준삼아 순환키를 업데이트 합니다.
  3. zd는 _ZipDecryptor 클래스의 개체입니다. pwd 변수는 __init__() 메소드로 전달되고 순환키를 처음으로 업데이트 하는 데 사용됩니다.
  4. zip 파일의 첫 12 바이트를 zd 개체에 매핑해서 해독합니다. 실질적으로, zd를 12번 "호출"해서 __call__() 메소드를 12번 호출하게 되고, 그에 따라 내부 상태를 저장하고 결과로 얻어진 바이트를 반화하는 일도 12번 하게 됩니다.

집합처럼 작동하는 클래스

여러분의 클래스가 여러 개의 값을 담는 기능을 한다면, 즉 여러분의 클래스가 어떤 값을 "가지고" 있는지 묻는 것이 말이 되는 상황이라면, 그 클래스가 집합처럼 작동하게 만들기 위해 아래와 같은 특수 함수를 정의해야 할 것입니다.

Notes You Want… So You Write… And Python Calls…
the number of items len(s) s.__len__()
to know whether it contains a specific value x in s s.__contains__(x)

cgi 모듈은 그 내부에 정의된 FieldStorage 클래스에서 이들 메소드를 사용합니다. FieldStorage 클래스는 동적인 웹페이지에 전송된 모든 폼(form) 필드나 질의(query) 인자를 저장합니다.

  1. cgi.FieldStorage 클래스의 개체를 생성하면, 특정 인자가 질의 문자열에 포함되어 있는지 확인하기 위해 "in" 연산자를 사용할 수 있습니다.
  2. __contains__() 메소드가 덕분에 이런 일이 가능합니다. if 'q' in fs라고 입력하면 파이썬은 cgi.py에 정의된 fs 객체의 __contains__() 메소드를 찾습니다. 'q'는 __contains__() 메소드에 key라는 인자로 전달됩니다.
  3. any() 함수는 제너레이터 표현식을 인자로 받아 그 제너레이터가 반환한 값의 하나라도 key와 일치하면 True를 돌려줍니다. any() 함수는 첫 번째 일치하는 항목이 발견되면 거기서 순환을 멈춥니다.
  4. FieldStorage 클래스는 저장하고 있는 항목의 길이를 반환하는 기능도 제공합니다. 그래서 len(fs)라고 명령을 내리면 FieldStorge 클래스에 정의된 __len__() 메소드를 호출해서 질의 항목의 개수를 반환합니다.
  5. self.keys() 메소드는 self.list is None의 값을 확인해서 __len__ 메소드에서 이 오류 확인을 다시 할 필요 없게 해줍니다.

사전처럼 작동하는 클래스

이전 섹션의 내용을 조금 더 연장해서, 클래스에 "in" 연산자나 len() 함수 뿐만 아니라 키(key)에 해당하는 값(value)을 반환하는 기능도 추가해, 완전한 사전처럼 작동하게 만들 수도 있습니다.

Notes You Want… So You Write… And Python Calls…
to get a value by its key x[key] x.__getitem__(key)
to set a value by its key x[key] = value x.__setitem__(key, value)
to delete a key-value pair del x[key] x.__delitem__(key)
to provide a default value for missing keys x[nonexistent_key] x.__missing__(nonexistent_key)

cgi 모듈에 정의된 FieldStorage 클래스는 아래에 보이는 특수 메소드도 정의하고 있습니다.

  1. fs 객체는 cgi.FieldStorage의 개체지만 여전히 fs['q']와 같은 표현식을 사용할 수 있습니다.
  2. fs['q']는 key인자에 'q'를 전달하면서 __getitem__() 메소드를 호출합니다. 그러면 내부적으로 저장하고 있는 질의 인자의 리스트(self.list)를 검색해서, item.name이 주어진 key와 일치하는 항목이 있는지 찾습니다.

숫자처럼 작동하는 클래스

적절한 특수 메소드를 사용해서 여러분의 클래스가 숫자처럼 작동하게 만들 수도 있습니다. 즉, 객체를 더하거나 뺄 수도 있고, 다른 수학 연산자도 적용할 수 있다는 말입니다. 분수도 이런 식으로 구현됩니다. Fraction 클래스는 아래 테이블에 있는 것과 같은 특수 함수를 구현해서 아래 예제와 같은 일을 할 수 있게 해줍니다.

숫자처럼 작동하는 클래스를 구현하기 위해 필요한 특수 함수의 리스트는 다음과 같습니다.

Notes You Want… So You Write… And Python Calls…
addition x + y x.__add__(y)
subtraction x - y x.__sub__(y)
multiplication x * y x.__mul__(y)
division x / y x.__truediv__(y)
floor division x // y x.__floordiv__(y)
modulo (remainder) x % y x.__mod__(y)
floor division & modulo divmod(x, y) x.__divmod__(y)
raise to power x ** y x.__pow__(y)
left bit-shift x << y x.__lshift__(y)
right bit-shift x >> y x.__rshift__(y)
bitwise and x & y x.__and__(y)
bitwise xor x ^ y x.__xor__(y)
bitwise or x | y x.__or__(y)

x라는 개체가 이들 메소드를 모두 구현하고 있는 클래스의 개체라면 별 문제가 없습니다. 하지만 클래스가 이 메소드 중 일부를 구현하지 않는 경우는 어떨까요? 혹은 구현이 되어 있기는 하지만 어떤 종류의 입력은 처리할 수 없는 경우라면요? 예를 들어보죠.

Fraction 개체를 생생해서 그 개체를 정수로 나누었던 이전 예제와 비슷해 보이지만 실제로는 비슷하지 않습니다. 이전 예제의 경우는 분명했습니다. x / 3이 x.__truediv__(3)을 호출했고, Fraction 클래스의 메소드인 __truediv__()가 모든 수학을 담당했습니다. 하지만 이 예제에서 정수(1)는 Fraction 개체(x)를 어떻게 나누어야 하는지 "알지" 못합니다. 그런데 왜 오류가 나지 않고 잘 작동할까요?

사실 거꾸로 된 피연산자(reflected operands)를 처리할 수 있는 두 번째 산술 연산 방법을 위한 특수 메소드가 따로 존재합니다. 다시 말해, 두 개의 피연산자를 취하는 산술 연산이 주어질 때(예를 들어 x / y) 이 연산을 수행하는 두 가지 방법이 존재합니다.

  1. x에게 자신을 y로 나누라고 말하거나,
  2. y에게 x에서 자신을 나누라고 합니다.

위의 리스트에 나와 있는 특수 메소드는 첫 번째 방법을 따릅니다. x / y가 주어지만 x에게 자신을 y로 나누라는 명령을 내립니다. 다음 리스트에 나와 있는 특수 메소드는 두 번째 방법을 따라갑니다. 즉, y에게 x에서 자신을 나누라는 명령을 내리는 것입니다.

Notes You Want… So You Write… And Python Calls…
addition x + y y.__radd__(x)
subtraction x - y y.__rsub__(x)
multiplication x * y y.__rmul__(x)
division x / y y.__rtruediv__(x)
floor division x // y y.__rfloordiv__(x)
modulo (remainder) x % y y.__rmod__(x)
floor division & modulo divmod(x, y) y.__rdivmod__(x)
raise to power x ** y y.__rpow__(x)
left bit-shift x << y y.__rlshift__(x)
right bit-shift x >> y y.__rrshift__(x)
bitwise and x & y y.__rand__(x)
bitwise xor x ^ y y.__rxor__(x)
bitwise or x | y y.__ror__(x)

잠시만요! 더 말해야 할 것이 있습니다. 만약 여러분이 x /= 3과 같은 연산(in-place 연산)을 수행한다면, 이를 위한 특수 메소드도 따로 정의할 수 있습니다.

Notes You Want… So You Write… And Python Calls…
in-place addition x += y x.__iadd__(y)
in-place subtraction x -= y x.__isub__(y)
in-place multiplication x *= y x.__imul__(y)
in-place division x /= y x.__itruediv__(y)
in-place floor division x //= y x.__ifloordiv__(y)
in-place modulo x %= y x.__imod__(y)
in-place raise to power x **= y x.__ipow__(y)
in-place left bit-shift x <<= y x.__ilshift__(y)
in-place right bit-shift x >>= y x.__irshift__(y)
in-place bitwise and x &= y x.__iand__(y)
in-place bitwise xor x ^= y x.__ixor__(y)
in-place bitwise or x |= y x.__ior__(y)

대부분의 경우 이런 in-place 연산 메소드는 필요하지 않습니다. 특정 연산에 이런 메소드를 정의하지 않더라도 파이썬이 알아서 처리합니다. 예를 들어, x /= y를 실행하면 파이썬은 다음과 같은 순서의 행동을 합니다.

  1. x.__itruediv__(y)를 호출해봅니다. 이 메소드가 정의되어 있고 NotImplemented가 아닌 값을 반환하면 그걸로 됐습니다.
  2. x.__truediv__(y)를 호출해봅니다. 이 메소드가 정의되어 있고 NotImplemented가 아닌 값을 반환하면, x = x / y 연산을 수행한 것처럼 x에 반환된 새로운 값이 할당됩니다.
  3. y.__rtruediv__(x)를 호출해봅니다. 이 메소드가 정의되어 있고 NotImplemented가 아닌 값을 반환하면, x에 반환된 새로운 값이 할당됩니다.

따라서 어떤 특별한 최적화 연산을 수행할 필요가 있을 때만 __itruediv__()와 같은 in-place 메소드를 정의하면 됩니다. 그렇지 않은 경우에는 파이썬이 in-place 연산을 적절하게 일반 연산으로 바꾸어 잘 처리할 수 있습니다.

숫자처럼 작동하는 객체에 "단일(unary)" 수학 연산을 수행할 수 있는 몇 가지 메소드도 존재합니다.

Notes You Want… So You Write… And Python Calls…
negative number -x x.__neg__()
positive number +x x.__pos__()
absolute value abs(x) x.__abs__()
inverse ~x x.__invert__()
complex number complex(x) x.__complex__()
integer int(x) x.__int__()
floating point number float(x) x.__float__()
number rounded to nearest integer round(x) x.__round__()
number rounded to nearest n digits round(x, n) x.__round__(n)
smallest integer >= x math.ceil(x) x.__ceil__()
largest integer <= x math.floor(x) x.__floor__()
truncate x to nearest integer toward 0 math.trunc(x) x.__trunc__()
PEP 357 number as a list index a_list[x] a_list[x.__index__()]

비교할 수 있는 클래스

비교 연산은 엄격히 말해 숫자의 영역이라고 할 수는 없으므로, 이 섹션을 이전 섹션과 분리했습니다. 많은 데이터형이 비교될 수 있습니다. 문자열, 리스트, 심지어 사전도요. 여러분이 만드는 클래스의 개체끼리 비교할 수 있는 경우라고 생각된다면, 다음 특수 메소드를 이용해서 비교 연산을 구현할 수 있습니다.

Notes You Want… So You Write… And Python Calls…
equality x == y x.__eq__(y)
inequality x != y x.__ne__(y)
less than x < y x.__lt__(y)
less than or equal to x <= y x.__le__(y)
greater than x > y x.__gt__(y)
greater than or equal to x >= y x.__ge__(y)
truth value in a boolean context if x: x.__bool__()

__lt__() 메소드만 정의하고 __gt__() 메소드를 정의하지 않으면, 파이썬은 피연산자의 위치를 바꿔서 __lt__() 메소드를 사용합니다. 하지만 파이썬이 메소드를 연결해서 연산하지는 않습니다. 예를 들어, __lt__() 메소드와 __eq__() 메소드를 정의하고 x <= y를 테스트 한다고 해봅시다. 파이썬은 __lt__()와 __eq__() 메소드를 연달아 호출하는 대신 그냥 __le__() 메소드를 호출합니다.

직렬화(serialised)될 수 있는 클래스

파이썬은 임의의 객체를 직렬화(serializing)하거나 역직렬화(unserializing)하는 방법을 지원합니다. (대부분의 파이썬 문서는 이 과정을 "피클링(pickling)"과 "역피클링(unpicking)"이라고 부릅니다.) 이를 방법은 상태를 파일에 저장하고 나중에 불러올 때 유용합니다. 모든 기본 데이터형은 피클링을 이미 지원하고 있습니다. 피클링을 지원하는 사용자 클래스를 만들고 싶다면 피클 프로토콜을 읽어보고 다음 특수 메소드를 언제 어떻게 호출하는지 살펴보면 됩니다.

Notes You Want… So You Write… And Python Calls…
a custom object copy copy.copy(x) x.__copy__()
a custom object deepcopy copy.deepcopy(x) x.__deepcopy__()
* to get an object’s state before pickling pickle.dump(x, file) x.__getstate__()
* to serialize an object pickle.dump(x, file) x.__reduce__()
* to serialize an object (new pickling protocol) pickle.dump(x, file, protocol_version) x.__reduce_ex__(protocol_version)
* control over how an object is created during unpickling x = pickle.load(file) x.__getnewargs__()
* to restore an object’s state after unpickling x = pickle.load(file) x.__setstate__()
  • 직렬화된 객체를 다시 생성하기 위해서 파이썬은 직렬화된 객체처럼 보이는 새로운 객체를 생성하고새 객체의 모든 속성을 설정합니다. __getnewargs__() 메소드는 객체가 생성되는 방법을 제어하고, __setstate__() 메소드는 속성의 값이 복원되는 방법을 제어합니다.

with 블록과 함께 사용할 수 있는 클래스

with 블록은 런타임 콘텍스트(runtime context)를 정의합니다. with 문을 실행하면 여러분은 콘텍스트로 "들어가고", 블록의 마지막 구문을 실행한 뒤에는 콘텍스트를 "빠져나옵니다".

Notes You Want… So You Write… And Python Calls…
do something special when entering a withblock with x: x.__enter__()
do something special when leaving a withblock with x: x.__exit__(exc_type,exc_value,traceback)

with 파일 구문이 어떻게 작동하는지 봅시다.

  1. 파일 객체는 __enter__()와 __exit__() 메소드를 정의하고 있습니다. __enter__() 메소드는 파일이 열렸는지 확인하고, 그렇지 않을 경우 _checkClosed() 메소드가 예외를 발생시킵니다.
  2. __enter__() 메소드는 거의 언제나 반드시 self를 반환해야 합니다. 여기서 self는 with 블록에서 속성과 메소드를 가져오기 위해 사용하는 객체입니다.
  3. with 블록이 끝나면 파일 객체는 자동으로 닫힙니다. 어째서 그렇게 될까요? __exit__() 메소드에서 self.close()를 호출하기 때문입니다.

__exit__() 메소드는 with 블록에서 예외가 발생하더라도 항상 호출됩니다. 사실 예외가 발생하면, 그 예외에 대한 정보가 __exit__() 메소드로 전달됩니다. 더 자세한 사항은 with 구문 콘텍스트 관리자를 참고하세요.

콘텍스트 관리자에 대해 더 알고 싶으시면 파일을 자동으로 닫기표준출력으로 리다이렉트 하기를 참고하세요.

정말 난해한 것들

여러분이 무엇을 하고 있는지 잘 알고 있기만 하다면, 클래스가 비교되는 법, 속성이 정의되는 법, 어떤 종류의 클래스가 여러분이 작성한 클래스의 서브클래스가 되는지와 같은 문제에 대해 완전한 제어를 할 수 있습니다.

Notes You Want… So You Write… And Python Calls…
a class constructor x = MyClass() x.__new__()
* a class destructor del x x.__del__()
only a specific set of attributes to be defined x.__slots__()
a custom hash value hash(x) x.__hash__()
to get a property’s value x.color type(x).__dict__['color'].__get__(x, type(x))
to set a property’s value x.color = 'PapayaWhip' type(x).__dict__['color'].__set__(x, 'PapayaWhip')
to delete a property del x.color type(x).__dict__['color'].__del__(x)
to control whether an object is an instance of your class isinstance(x, MyClass) MyClass.__instancecheck__(x)
to control whether a class is a subclass of your class issubclass(C, MyClass) MyClass.__subclasscheck__(C)
to control whether a class is a subclass of your abstract base class issubclass(C, MyABC) MyABC.__subclasshook__(C)

더 읽을거리

부록에서 언급된 모듈:

기타 가볍게 읽어볼 만한 글들:


이 강의는 영어로 된 원문을 기초로 작성되었으며, Creative Commons Attribution Share-Alike 라이센스하에 자유로운 변경, 배포가 가능합니다

4318 읽음
이전 부록 A. 2to3를 이용해서 코드를 파이썬 3로 이식하기
다음 부록 C. 이제 어디로 가야 할까요?

저자

토론이 없습니다

Please log in to leave a comment

16.5.11618.20190612.allo
문제 보고 · 사용조건 · 개인정보보호
래블업 주식회사 · 대한민국 서울 테헤란로 145 · 대표: 신정규 · 사업자번호: 864-88-00080 · +82 70-8200-2587

거절 확인

닫기
좋아요 책갈피 토론

아래 주소를 복사하세요