지난 포스트에 이어서 '파이썬 코딩의 기술' 책의 6장인 메타 클래스와 애트리뷰트에 대해 마저 기술해보도록 하겠다.
- 메타클래스
- __init__subclass__
- __set_name__
- 클래스 데코레이터
0. 간단 정의
클래스 애트리뷰트 : 클래스 내 self가 붙어있는 친구들이라 보면 편하다. 클래스 내 object라고 보면 될 듯.
메타클래스 : 클래스를 넘어가는 개념, 클래스 문을 가로채서 특별한 동작을 진행할 수 있음. ex) 클래스 잘 구현됐는지 검증
1. 메타클래스
클래스가 잘 구현됐는지 검증하기 위해 메타클래스를 사용할 수 있다. __init__메서드에서 검증 코드를 실행하는 경우가 많은데, 이때의 장점은 클래스 모듈을 import 할 때와 같은 시점에 검증 코드가 실행되게 때문에 예외를 더 빨리 찾을 수 있다.
메타클래스는 아래와 같이 정의할 수 있다. type을 상속받고, __new__메서드로 클래스 내용을 전달받는다.
- 메타클래스 사용법 1 : 클래스 검증
class MetaClass(type): #type을 상속해 정의
def __new__(meta, name,bases, class_dict): #new 메서드로 자신과 연관된 클래스 내용 전달받음
print(f'name {name}, meta {meta}')
print(f'bases {bases}, class dict {class_dict}')
if class_dict['cnt'] >1:
raise ValueError('cnt 값 너무큼')
class MyClass(metaclass = MetaClass):
cnt = 1
print(cnt)
#1
#name MyClass, meta <class '__main__.MetaClass'>
#bases (), class dict {'__module__': '__main__', '__qualname__': 'MyClass', 'cnt': 1}
#
#
위와 같이 메타클래스에서 클래스들의 정보를 받아와, 검증에 사용할 수 있다.
- 단점: 한 클래스당 한 메타클래스만 사용할 수 있다.
2. __init_subclass__
위에서 메타클래스를 정의하고 사용하는 법에 대해 알아보는데, __init_subclass__를 사용하면 더 단순하게 클래스 검증에 사용할 수 있다.
class LikeMetaClass:
def __init_subclass__(cls):
super().__init_subclass__()
if cls.cnt >1:
raise ValueError('cnt값 너무 큼')
@classmethod
def get_cnt(cls):
print(cls.cnt)
class MyClass2(LikeMetaClass):
cnt = 3
#Input In [9], in LikeMetaClass.__init_subclass__(cls)
# 3 super().__init_subclass__()
# 4 if cls.cnt >1:
#----> 5 raise ValueError('cnt값 너무 큼')
#
#ValueError: cnt값 너무 큼
3. __set_name__
클래스 애트리뷰트를 표시하는 방법 중 하나. 메타클래스 __new__ 에서 처리하는 일을 처리할 수 있는데 그 중 프로퍼티 이름을 변경할 수 있다. 어떻게 사용하는지만 알아두자.
class Field:
def __set_name__(self, owner, name):
self.name = name
self.internal_name = '_'+name
4. 클래스 데코레이터
functools.wrap으로 데코레이터를 생성할 수 있는데, 데코레이터를 각 메서드에 사용할 경우는 메서드마다 데코레이터를 써줘야 한다. 이러한 중복이 일어나기 때문에 가독성이 나빠진다.
from functools import wraps
#wrap으로 데코레이터 사용하는 예시
def trace_fcn(func):
if hasattr(func, 'tracing'):
return func
@wraps(func)
def wrapper(*args, **kwargs):
result = None
try :
result = func(*args, **kwargs)
return
except Exception as e:
result = e
raise
wrapper.tracing = True
print(func.__name__)
return wrapper
이럴 때 메타클래스 내부에서 wrap을 계속 호출할 수 있다.
import types
class TraceMeta(type):
def __new__(meta, name, bases, class_dict):
clas = super().__new__(meta, name, bases, class_dict)
for k in dir(clas):
value= getattr(clas, k)
#print(value)
if isinstance(value, types.BuiltinMethodType):
wrapped = trace_fcn(value)
print(wrapped)
setattr(clas, k, wrapped)
return clas
하지만 메타클래스를 여러개 사용할 수 없는 단점이 적용되기독 하고, 메타클래스를 사용하는 방법은 제약이 많은 편이라 클래스 데코레이터를 사용하는 편이 더 낫다.
#클래스 데코레이터 사용법
def class_decorator(clas):
for k in dir(clas):
value= getattr(clas, k)
#print(value)
if isinstance(value, types.BuiltinMethodType):
wrapped = trace_fcn(value)
print(wrapped)
setattr(clas, k, wrapped)
return clas
@class_decorator
class MyClass:
pass
이번 장은 내가 개발할 때 사용할만한 직접적인 지식은 아니어서, 1)메타클래스와 애트리뷰트의 개념과 2) 사용법을 간략하게 알아보는 정도로 기록했다. 감만 잡고 나중에 필요할 때 더 깊이 공부해보도록 하자.
'python > 파이썬 코딩의 기술' 카테고리의 다른 글
Python Asyncio (0) | 2022.03.05 |
---|---|
Thread - 파이썬 코딩의 기술 리뷰[동시성과 병렬성 2] (0) | 2022.03.01 |
파이썬 리스트 append 연산 시 arr와 arr[:] 차이점 (0) | 2022.02.12 |
파이썬 코딩의 기술 리뷰 - 메타클래스와 애트리뷰트 1 (0) | 2022.02.01 |
파이썬 코딩의 기술 리뷰 - 클래스, 인터페이스 (0) | 2022.01.16 |