<6. Metaclasses and Attributes>
Item 49: Register Class Existence with __init_subclass__Better way 49. __init_subclass__를 사용해 클래스 확장을 등록하라
1. 한 줄 요약 및 첨언
__init_subclass__ 통해서 하위 클래스 정의하면 각 클래스마다 꼭 불려야 하는 로직을 자동으로 수행할 수 있습니다.
2. 사용 예시
객체를 json string 으로 바꾸기도 하고, 해당 string으로부터 다시 객체로 만드는 기능이 필요한 상황입니다.
클래스 등록 부분입니다. registry 변수에 하위 클래스들의 이름을 등록하고, deserialize때 사용하려는 목적입니다.
importjson# Example 6registry={}# 클래스 이름 저장용 딕셔너리# 클래스 이름 등록하는 함수. 클래스 정의때 요게 불려야 함defregister_class(target_class):registry[target_class.__name__]=target_class# json str 으로부터 다시 객체 만들어주는 함수defdeserialize(data:str):params=json.loads(data)name=params['class']target_class=registry[name]returntarget_class(*params['args'])# *params['args']: 리스트의 원소들을 arguments 로 넘김
베이스가 되는 클래스입니다. __init_subclass__ 를 구현해서, register_class()가 자동으로 호출이 됩니다. (수동으로 호출할 필요가 없어 졌습니다.)
# Example 5 + Example 13classBetterSerializable:# __init_class__ 구현한 베이스 클래스def__init__(self,*args):self.args=argsdefserialize(self):returnjson.dumps({'class':self.__class__.__name__,'args':self.args,})def__repr__(self):name=self.__class__.__name__args_str=', '.join(str(x)forxinself.args)returnf'{name}({args_str})'def__init_subclass__(cls):# 요 메서드 구현super().__init_subclass__()register_class(cls)# 하위 클래스가 정의 될 때, 요게 자동으로 불리도록 하기 위함.
Class registration is a helpful pattern for building modular Python programs.
클래스 등록은 파이썬 프로그램을 모듈화할 때 유용한 패턴이다.
Tip
Metaclasses let you run registration code automatically each time a base class is subclassed in a program.
메타클래스를 사용하면, 프로그램 안에서 기반 클래스를 상속한 하위 클래스가 정의될 때마다 등록 코드를 자동으로 실행할 수 있다.
Tip
Using metaclasses for class registration helps you avoid errors by ensuring that you never miss a registration call.
메타클래스를 클래스 등록에 사용하면 클래스 등록 함수를 호출하지 않아서 생기는 오류를 피할 수 있다.
Tip
Prefer __init_subclass__ over standard metaclass machinery because it’s clearer and easier for beginners to understand.
표준적인 메타클래스 방식보다는 __init_sublcass__가 더 낫다. __init_subclass__ 쪽이 더 깔끔하고 초보자가 이해하기도 더 쉽다.
4. 더 알아보기
4.1 메타클래스를 사용했다면
메타클래스를 사용한 방식도 알아두기는 합시다.
# 클래스 등록 부분은 같은 코드 사용# Example 11classMeta(type):def__new__(meta,name,bases,class_dict):cls=type.__new__(meta,name,bases,class_dict)register_class(cls)# 하위 클래스가 정의 될 때, 요게 자동으로 불리도록 하기 위함.returnclsclassTempSerializable(metaclass=Meta):# metaclass 지정def__init__(self,*args):self.args=argsdefserialize(self):returnjson.dumps({'class':self.__class__.__name__,'args':self.args,})def__repr__(self):name=self.__class__.__name__args_str=', '.join(str(x)forxinself.args)returnf'{name}({args_str})'# Example 12classVector3D(TempSerializable):def__init__(self,x,y,z):super().__init__(x,y,z)self.x,self.y,self.z=x,y,zbefore=Vector3D(10,-7,3)print('Before: ',before)# Before: Vector3D(10, -7, 3)data=before.serialize()print('Serialized:',data)# Serialized: {"class": "Vector3D", "args": [10, -7, 3]}print('After: ',deserialize(data))# After: Vector3D(10, -7, 3)