카테고리 없음

스파르타 AI-8기 TIL(1/30)-개인 공부

kimjunki-8 2025. 1. 30. 23:21
메타클래스(Metaclass)
한 마디로 클래스를 만드는 클래스이며, 파이썬에서는 클래스도 객체입니다. 그럼 클래스를 만드는 객체는 무엇일까요? 바로 메타클래스입니다! 참고로 객체(Object)란 프로그래밍에서 데이터(속성, Attribute)와 그 데이터를 처리하는 동작(메서드, Method)을 함께 포함하는 하나의 독립적인 단위입니다.
클래스와 객체의 관계 다시 보기
파이썬에서 모든 객체는 어떤 클래스의 인스턴스입니다.
그런데 클래스도 객체입니다.
class Dog:
    pass

d = Dog()  
print(type(d))  # <class '__main__.Dog'>
print(type(Dog))  # <class 'type'>

즉,

num = 10           # 정수 객체
text = "Hello"     # 문자열 객체
lst = [1, 2, 3]    # 리스트 객체
def func():        # 함수도 객체!
    return "Hi"
class Dog:         # 클래스도 객체!
    pass


여기서 인스턴스와 객체의 차이를 본다면,
객체(Object) → 프로그래밍에서 데이터와 기능을 함께 가진 모든 것 즉, 파이썬에서는 모든 것이 객체라고 볼 수 있습니다.
인스턴스(Instance)특정 클래스(Class)에서 생성된 객체
"모든 인스턴스는 객체이지만, 모든 객체가 인스턴스는 아니다!"

num = 10
text = "Hello"
lst = [1, 2, 3]

num, text, lst도 객체(Object)입니다.
하지만 우리가 만든 클래스(Dog)에서 생성된 것이 아니므로 "인스턴스"는 아닙니다.

class Dog:
    pass

d = Dog()  
print(type(d))  # <class '__main__.Dog'>

d는 Dog 클래스의 인스턴스입니다.
즉, Dog는 객체를 만들기 위한 틀(클래스) 역할을 합니다.


일단 본론으로 들어와서 type()이 메타클래스인것 아시나요?
type()이 "타입(클래스)을 확인하는 함수"라고 알고 있었는데,
type()은 두 가지 역할을 한다! 파이썬에서 type()은 오버로딩된 함수처럼 두 가지 역할을 합니다.

type(obj) → 객체의 클래스(타입)를 반환 가장 익숙한 사용법은 객체의 타입(클래스)을 확인하는 것입니다.
print(type(10))      # <class 'int'>
print(type("hello")) # <class 'str'>
print(type([]))      # <class 'list'>​

type(obj)는 객체의 클래스(타입)를 반환합니다.
여기서는 우리가 흔히 아는 "타입 확인 함수"로 동작합니다.


type(name, bases, dict) → 새로운 클래스를 생성 type()은 클래스를 동적으로 생성하는 메타클래스 역할도 합니다.
MyClass = type("MyClass", (object,), {"x": 10})

print(MyClass)    # <class '__main__.MyClass'>
print(MyClass.x)  # 10​

 

type(name, bases, dict)에서 bases와 dict란?
MyClass = type("MyClass", (object,), {"x": 10})
"MyClass" → 클래스 이름
(object,) → 부모 클래스(상속할 클래스)
{"x": 10} → 클래스 속성(딕셔너리 형태로 정의)​

 

bases → 부모 클래스(상속 관계)
bases는 튜플 형태로 클래스를 상속받을 부모 클래스를 지정합니다.즉, 어떤 클래스를 상속할지를 결정합니다.
class Parent:
    pass

Child = type("Child", (Parent,), {})  # Parent를 상속

print(issubclass(Child, Parent))  # True​

Child는 Parent를 상속받았으므로 issubclass(Child, Parent)가 True를 반환합니다.


bases 없이 만들면?
bases에 아무것도 지정하지 않으면 상속이 안 되므로 에러가 납니다.
# type("MyClass", {}, {})  # TypeError: bases must be tuple​
bases는 반드시 튜플로 감싸야 합니다.
만약 상속할 부모 클래스가 없으면 (object,)를 넣어 기본 클래스로 설정합니다.

dict → 클래스 속성과 메서드 정의
dict는 클래스 내부에 들어갈 속성(attribute)과 메서드(method)를 정의하는 딕셔너리입니다.
MyClass = type("MyClass", (object,), {"x": 10, "greet": lambda self: "안녕!"})

obj = MyClass()
print(obj.x)       # 10
print(obj.greet()) # "안녕!"​

 

클래스 내부에 x = 10 속성과 greet() 메서드를 정의했습니다.
lambda self: "안녕!"은 메서드를 나타냅니다.

dict 없이 만들면?
dict를 {}로 비워두면 속성이 없는 빈 클래스가 됩니다.

EmptyClass = type("EmptyClass", (object,), {})
print(dir(EmptyClass))  # 속성이 거의 없음

 

# (1) 일반적인 클래스 정의
class MyClass:
    x = 10
    
    def greet(self):
        return "안녕!"

# (2) type()을 사용해 같은 클래스 만들기
MyClass = type("MyClass", (object,), {"x": 10, "greet": lambda self: "안녕!"})

두 코드의 결과는 동일합니다.
type()을 사용하면 동적으로 클래스를 생성할 수 있음을 알 수 있습니다.

결국, 메타클래스는 "클래스를 만드는 클래스"입니다.
즉, 메타클래스는 클래스의 생성 과정을 제어할 수 있는 특별한 클래스입니다.

우리가 일반적으로 클래스를 정의할 때 사용하는 class는 메타클래스에 의해 만들어집니다.
type은 기본적인 메타클래스로, 우리가 클래스를 정의할 때 사용되는 기본 메타클래스 역할을 합니다.
일단 이렇게만 해놓고 자세한건 내일..