ko ko ko en en en ko ko cs zh ko ko zh ko ko ko ko ko ko ko ko ko ko ko ko ko hbs_lat ko ko ko ko ko en ko ko ko ko eo ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko ko nb ko ko ko ko ko ko ko ko ko ko ko ko fr ko ko ko ko fr ko ko ko en en ko en zh en en
파이썬 코딩 도장: 47.9 메타클래스 사용하기
◀ 47.8 with as에 사용할 수 있는 클래스 만들기47.10 asyncio 사용하기 ▶︎
메타클래스(metaclass)는 클래스를 만드는 클래스인데, 이 메타클래스를 구현하는 방법은 두 가지가 있습니다.
type을 사용하여 동적으로 클래스를 생성하는 방식
type을 상속받아서 메타클래스를 구현하는 방식
type은 객체의 클래스(자료형) 종류를 알아낼 때도 사용할 수 있고, 클래스를 만들어낼 수도 있습니다.
47.9.1  type을 사용하여 동적으로 클래스 생성하기
먼저 클래스는 type 안에 클래스 이름(문자열), 기반 클래스 튜플, 속성과 메서드 딕셔너리를 지정해서 만듭니다.
클래스 = type('클래스이름', 기반클래스튜플, 속성메서드딕셔너리)
type('Hello', (), {})는 클래스 이름이 Hello고 기반 클래스와 속성, 메서드가 없는 클래스를 만듭니다. 이렇게 만든 Hello 클래스는 인스턴스를 생성할 수 있습니다.
그럼 클래스에 속성과 메서드를 넣고 상속도 사용해보겠습니다. 다음은 list를 상속받고, 속성 desc와 메서드 replace가 들어있는 클래스를 만듭니다.
def replace(self, old, new):    # 클래스에 들어갈 메서드 정의     while old in self:         self[self.index(old)] = new   # list를 상속받음, 속성 desc, 메서드 replace 추가 AdvancedList = type('AdvancedList', (list, ), { 'desc': '향상된 리스트', 'replace': replace })   x = AdvancedList([1, 2, 3, 1, 2, 3, 1, 2, 3]) x.replace(1, 100) print(x)         # [100, 2, 3, 100, 2, 3, 100, 2, 3] print(x.desc)    # 향상된 리스트
이렇게 type의 기반 클래스 튜플에 기반 클래스를 지정하고, 속성과 메서드 딕셔너리에 속성과 메서드를 넣으면 됩니다. 이때 기반 클래스가 한 개이면 (list, )와 같이 튜플로 만들어줍니다. 그리고 속성과 메서드를 딕셔너리에 넣을 때 이름은 반드시 문자열 형태로 지정해줍니다. 그리고 메서드가 간단하다면 def로 함수를 작성하지 않고 람다 표현식을 사용해도 됩니다.
47.9.2  type을 상속받아서 메타클래스 구현하기
이번에는 메타클래스의 __new__ 메서드를 알아보겠습니다. 클래스가 type을 상속받으면 메타클래스가 됩니다. 이때 __new__ 메서드에서 새로 만들어질 클래스에 속성과 메서드를 추가해줄 수 있습니다.
class 메타클래스이름(type):     def __new__(metacls, name, bases, namespace):         코드
class MakeCalc(type):    # type을 상속받음     def __new__(metacls, name, bases, namespace):      # 새 클래스를 만들 때 호출되는 메서드         namespace['desc'] = '계산 클래스'              # 새 클래스에 속성 추가         namespace['add'] = lambda self, a, b: a + b    # 새 클래스에 메서드 추가         return type.__new__(metacls, name, bases, namespace)    # type의 __new__ 호출   Calc = MakeCalc('Calc', (), {})    # 메타클래스 MakeCalc로 클래스 Calc 생성 c = Calc()                         # 클래스 Calc로 인스턴스 c 생성 print(c.desc)                      # '계산 클래스': 인스턴스 c의 속성 출력 print(c.add(1, 2))                 # 3: 인스턴스 c의 메서드 호출
__new__ 메서드는 Calc = MakeCalc('Calc', (), {})처럼 메타클래스로 새 클래스를 만들 때 호출됩니다. 따라서 이 메서드 안에서 새 클래스에 속성과 메서드를 추가해줄 수 있습니다. 여기서는 lambda self, a, b: a + b와 같이 간단하게 람다 표현식으로 메서드를 추가했습니다. 특히 메서드의 첫 번째 매개변수는 반드시 self라야 하므로 람다 표현식에서도 self를 지정해주어야 합니다.
메타클래스는 주로 클래스의 동작을 제어할 때 사용합니다. 다음은 싱글톤(Singleton)이라는 방식인데 클래스의 인스턴스를 언제나 하나만 생성해냅니다.
class Singleton(type):    # type을 상속받음     __instances = {}      # 클래스의 인스턴스를 저장할 속성     def __call__(cls, *args, **kwargs):    # 클래스로 인스턴스를 만들 때 호출되는 메서드         if cls not in cls.__instances:     # 클래스로 인스턴스를 생성하지 않았는지 확인             cls.__instances[cls] = super().__call__(*args, **kwargs)                                            # 생성하지 않았으면 인스턴스를 생성하여 속성에 저장         return cls.__instances[cls]        # 클래스로 인스턴스를 생성했으면 인스턴스 반환   class Hello(metaclass=Singleton):    # 메타클래스로 Singleton을 지정     pass   a = Hello()     # 클래스 Hello로 인스턴스 a 생성 b = Hello()     # 클래스 Hello로 인스턴스 b 생성 print(a is b)   # True: 인스턴스 a와 b는 같음
먼저 type을 상속받은 메타클래스 Singleton을 만들고, 클래스 Hello를 만들 때 class Hello(metaclass=Singleton):와 같이 metaclass에 Singleton을 지정합니다. 이렇게 하면 메타클래스 Singleton이 클래스 Hello의 동작을 제어할 수 있습니다.
보통 __call__ 메서드는 인스턴스를 ( )(괄호)로 호출할 때 호출됩니다. 하지만 type을 상속받은 메타클래스에서 __call__ 메서드를 구현하면 메타클래스를 사용하는 클래스로 인스턴스를 만들 때 __call__ 메서드가 호출됩니다(클래스를 ( )로 호출할 때).
여기서는 Hello()로 인스턴스를 만들 때 Singleton의 __call__ 메서드가 호출됩니다. 따라서 __call__ 안에서 이미 인스턴스가 생성되지 않았는지(중복되는지) 확인하고, 생성되지 않았으면 인스턴스를 생성하여 속성에 저장한 뒤 반환합니다. 만약 인스턴스가 생성되어 있다면(중복된다면) 인스턴스를 생성하지 않고 바로 반환합니다.
대표적인 메타클래스가 '36.6 추상 클래스 사용하기'에서 설명한 abc.ABCMeta입니다. abc.ABCMeta를 사용한 추상 클래스는 메서드 목록만 가지도록 만들고, 파생 클래스에서 메서드 구현을 강제합니다. 즉, 메타클래스로 클래스의 동작을 제어합니다.
◀ 47.8 with as에 사용할 수 있는 클래스 만들기47.10 asyncio 사용하기 ▶︎
Unit 1. 소프트웨어 교육과 파이썬
Unit 2. 파이썬 설치하기
Unit 3. Hello, world!로 시작하기
Unit 4. 기본 문법 알아보기
Unit 5. 숫자 계산하기
Unit 6. 변수와 입력 사용하기
Unit 7. 출력 방법 알아보기
Unit 8. 불과 비교, 논리 연산자 알아보기
Unit 9. 문자열 사용하기
Unit 10. 리스트와 튜플 사용하기
Unit 11. 시퀀스 자료형 활용하기
Unit 12. 딕셔너리 사용하기
Unit 13. if 조건문으로 특정 조건일 때 코드 실행하기
Unit 14. else를 사용하여 두 방향으로 분기하기
Unit 15. elif를 사용하여 여러 방향으로 분기하기
Unit 16. for 반복문으로 Hello, world! 100번 출력하기
Unit 17. while 반복문으로 Hello, world! 100번 출력하기
Unit 18. break, continue로 반복문 제어하기
Unit 19. 계단식으로 별 출력하기
Unit 20. FizzBuzz 문제
Unit 21. 터틀 그래픽스로 그림 그리기
Unit 22. 리스트와 튜플 응용하기
Unit 23. 2차원 리스트 사용하기
Unit 24. 문자열 응용하기
Unit 25. 딕셔너리 응용하기
Unit 26. 세트 사용하기
Unit 27. 파일 사용하기
Unit 28. 회문 판별과 N-gram 만들기
Unit 29. 함수 사용하기
Unit 30. 함수에서 위치 인수와 키워드 인수 사용하기
Unit 31. 함수에서 재귀호출 사용하기
Unit 32. 람다 표현식 사용하기
Unit 33. 클로저 사용하기
Unit 34. 클래스 사용하기
Unit 35. 클래스 속성과 정적, 클래스 메서드 사용하기
Unit 36. 클래스 상속 사용하기
Unit 37. 두 점 사이의 거리 구하기
Unit 38. 예외 처리 사용하기
Unit 39. 이터레이터 사용하기
Unit 40. 제너레이터 사용하기
Unit 41. 코루틴 사용하기
Unit 42. 데코레이터 사용하기
Unit 43. 정규표현식 사용하기
Unit 44. 모듈과 패키지 사용하기
Unit 45. 모듈과 패키지 만들기
Unit 46. 실전예제: 웹의 데이터로 그래프 그리기
47.1 비트 연산자 사용하기
47.3 bytes, bytearray 사용하기
47.4 날짜/시간 모듈 활용하기
47.7 실수 값의 오차
47.8 with as에 사용할 수 있는 클래스 만들기
47.12 Windows에서 패키지 설치 에러 해결하기
C 언어 코딩 도장
- 이 사이트에 실린 모든 내용, 디자인, 이미지, 편집 구성의 저작권은 (주)도서출판 길벗과 지은이에게 있습니다.
- 저작권법에 의해 보호를 받는 저작물이므로 무단 전재와 복제를 금합니다.
- 책의 내용을 복제하여 블로그, 웹사이트 등에 게시할 수 없습니다.
- 링크, SNS 공유는 허용합니다.
서울시 마포구 서교동 월드컵로 10길 56 (서울시 마포구 서교동 467-9)
Copyright © 2016-2021 Coding Dojang