#44. クラスとメタクラス

クラスは、Python オブジェクト モデルを通じてインスタンスを作成し、属性を保存し、継承に参加し、動作を定義するランタイム オブジェクトです。 CPython では、クラスは通常、型が次のようなオブジェクトです。type

メタクラスはクラスのクラスです。これは、クラス オブジェクトの作成、初期化、表現、呼び出しの方法を制御します。

通常クラスの場合:```python id="g3fkzi" class User: pass

print(type(User)) 出力:text id="iqt3cc" <class 'type'> ```これはつまりUserはオブジェクトであり、その型はtype

44.1 クラスはオブジェクトである

クラス定義によりクラス オブジェクトが作成されます。```python id="8zc5m6" class User: name = "anonymous"

def hello(self):
    return "hello"

処刑後は、`User`周囲の名前空間にバインドされた通常の名前です。python id="rx6c1m" print(User) print(type(User)) print(User.name) print(User.dict) クラス オブジェクトには次のような属性が格納されます。text id="xxewj0" name qualname module dict bases mro methods descriptors class variables annotations


## 44.2 クラス定義は実行です

クラス本体は実行可能なコードです。```python id="938wkx"
class Example:
    print("inside class body")
    x = 1 + 2
```定義中の出力:```text id="jiq7he"
inside class body
```クラス本体は、CPython が実行するとすぐに実行されます。`class`声明。インスタンスが作成されるまで遅延しません。

クラス本体内のトップレベルのコードはクラスの作成時に実行されるため、これは重要です。```python id="aj5kyg"
class Bad:
    data = load_large_file()
```この作業は、クラスが定義されるときに行われます。

## 44.3 クラス作成パイプライン

クラス定義:```python id="yzw9ss"
class User(Base):
    x = 1

    def hello(self):
        return "hello"
```概念的には次のものに似ています。```python id="g55w7a"
namespace = {}
namespace["x"] = 1
namespace["hello"] = function_object
User = type("User", (Base,), namespace)
```実際のプロセスにはさらに多くのステップがあります。```text id="zd6xb1"
1. Evaluate base classes.
2. Determine the metaclass.
3. Ask the metaclass for a class namespace.
4. Execute the class body in that namespace.
5. Create the class object.
6. Call descriptor __set_name__ methods.
7. Call subclass initialization hooks.
8. Bind the class object to its name.
```このパイプラインは、メタクラス、記述子、デコレータ、および継承がどのように相互作用するかを説明します。

## 44.4 基本クラスの評価

:```python id="zu9u35"
class User(Model):
    pass
```CPython は最初に評価します`Model`

基本クラスは式です。```python id="fnc7gv"
class User(get_base_class()):
    pass
```関数呼び出しは、クラス オブジェクトが作成される前に発生します。

複数の塩基は左から右に評価されます。```python id="qtk75l"
class C(A(), B()):
    pass
```結果として得られるオブジェクトは有効な基本クラスであるか、または次の方法で変換可能である必要があります。`__mro_entries__`

##44.5`__mro_entries__`フック`__mro_entries__`クラス作成時に非クラス基本オブジェクトがそれ自体を置き換えることができます。

これは、いくつかのタイピングや汎用の機械によって使用されます。

形状の例:```python id="esjxp2"
class BaseAlias:
    def __mro_entries__(self, bases):
        return (RealBase,)

class C(BaseAlias()):
    pass
```概念的には、CPython は次のようになります。```python id="154gd3"
class C(BaseAlias()):
    pass
```の中へ:```python id="kfk2ta"
class C(RealBase):
    pass
```継承目的のため。

ほとんどのアプリケーションコードは決して実装しない`__mro_entries__`, ただし、これはクラス作成の一部です。

## 44.6 メタクラスの決定

メタクラスは明示的に指定できます。```python id="o3wmta"
class User(metaclass=Meta):
    pass
```メタクラスが指定されていない場合、CPython は基本クラスからメタクラスを派生します。

通常クラスの場合:```python id="oe50zn"
class User:
    pass
```メタクラスは次のとおりです。```text id="ythfx3"
type
```サブクラスの場合:```python id="t5oe3k"
class Child(Base):
    pass
```メタクラスは通常、`type(Base)`または互換性のある派生メタクラス。

選択したメタクラスは、すべての基本クラスのメタクラスと互換性がある必要があります。そうしないと、CPython によってメタクラスの競合が発生します。

## 44.7 メタクラスの競合

メタクラスの競合は、基本クラスが互換性のないメタクラスを必要とする場合に発生します。

例:```python id="dk85qy"
class MetaA(type):
    pass

class MetaB(type):
    pass

class A(metaclass=MetaA):
    pass

class B(metaclass=MetaB):
    pass

class C(A, B):
    pass
```CPython は両方のメタクラスと互換性のある単一のメタクラスを選択できないため、これによりエラーが発生します。`MetaA`そして`MetaB`

通常の修正は、結合されたメタクラスを定義することです。```python id="7bm0x5"
class MetaC(MetaA, MetaB):
    pass

class C(A, B, metaclass=MetaC):
    pass
```メタクラスの競合は、メタクラスを使用するフレームワークを組み合わせるときによく発生します。

## 44.8 クラス名前空間の準備

クラス本体を実行する前に、CPython はメタクラスに名前空間を要求します。

これは呼び出しによって行われます`__prepare__`存在する場合。```python id="0o8dg6"
class Meta(type):
    @classmethod
    def __prepare__(mcls, name, bases, **kwargs):
        return {}

class User(metaclass=Meta):
    x = 1
```返されたオブジェクトは、クラス本体のローカル名前空間として使用されます。

歴史的には、これにより順序付けられたクラス名前空間が可能になりました。最新の辞書は挿入順序を保持しますが、`__prepare__`カスタム名前空間の動作は引き続きサポートされます。

ユースケースの例:```text id="rq5a86"
tracking declaration order
rejecting duplicate names
collecting field definitions
custom class DSLs
framework model declarations
```## 44.9 クラス本体の名前空間

クラス本体は、独自のローカル名前空間で実行されます。```python id="4q8zlf"
x = "global"

class Example:
    x = "class local"
    y = x

print(Example.x)
print(Example.y)
```出力:```text id="7d4l4l"
class local
class local
```クラス本体の代入は、インスタンスではなくクラス名前空間に書き込まれます。

クラス内の関数本体は、クラスローカル名を自動的に取得しません。```python id="kkxro6"
class Example:
    x = 10

    def method(self):
        return x
```通常、これはグローバルなグローバル変数がない限り、実行時に失敗します。`x`メソッドのグローバル ルックアップではクラス名前空間ではなくモジュール グローバルが使用されるためです。

正しい:```python id="mjh0gx"
class Example:
    x = 10

    def method(self):
        return self.x
```または:```python id="73a9fm"
class Example:
    x = 10

    def method(self):
        return type(self).x
```## 44.10 クラスオブジェクトの作成

クラス本体を実行した後、CPython はメタクラスを呼び出します。

通常のクラスの場合、これは次の呼び出しを意味します。`type`

概念的には:```python id="y5jzez"
User = type("User", bases, namespace)
```への呼び出し`type`を作成します`PyTypeObject`内部的に。

結果のクラス オブジェクトには以下が格納されます。```text id="q6gq90"
class name
base classes
method resolution order
class dictionary
type flags
slot tables
weakref support
instance layout
descriptor information
subclass relationships
```したがって、ユーザー定義クラスは型オブジェクトです。

##44.11`type(name, bases, namespace)`次のように手動でクラスを作成できます`type`。```python id="swqmpu"
def hello(self):
    return "hello"

User = type("User", (), {"hello": hello})

u = User()
print(u.hello())
```これは精神的には以下と同等です。```python id="owh4rx"
class User:
    def hello(self):
        return "hello"
````class`ステートメントは、構造化クラス作成プロトコルの構文です。

## 44.12 メタクラス`__new__`メタクラスは、オーバーライドすることでクラスの作成をカスタマイズできます。`__new__`。```python id="55v7xr"
class Meta(type):
    def __new__(mcls, name, bases, namespace, **kwargs):
        print("creating", name)
        return super().__new__(mcls, name, bases, namespace)

class User(metaclass=Meta):
    pass
```出力:```text id="xsdbla"
creating User

__new__クラスオブジェクトが存在する前に、クラス名、基本クラス、および名前空間を受け取ります。

使用__new__作成前にクラスを変更する必要がある場合:text id="e80nxe" modify namespace validate definitions inject methods collect metadata change base classes control class object allocation ## 44.13 メタクラス__init__メタクラスはオーバーライドすることでクラスの初期化をカスタマイズできます。__init__。```python id="o0xfzo" class Meta(type): def init(cls, name, bases, namespace, **kwargs): print("initializing", name) super().init(name, bases, namespace)

class User(metaclass=Meta): pass


`__init__`すでに作成されたクラスオブジェクトを次のように受け取ります`cls`

メタクラスを使用する`__init__`作成後にクラスを登録または検査する必要がある場合:```text id="u1q6kk"
register subclasses
validate final class
attach metadata
update external registries
```## 44.14 メタクラス`__call__`クラスの呼び出しは、そのメタクラスによって制御されます。

通常クラスの場合:```python id="kq4rc6"
u = User("Ada")
```によって処理されます:```text id="y1lk7w"
type(User).__call__(User, "Ada")
```通常のメタクラスの場合、`type.__call__`実行します:```text id="vxizzk"
1. call User.__new__(User, ...)
2. if result is an instance of User, call User.__init__(instance, ...)
3. return instance
```メタクラスはオーバーライドできる`__call__`:

```python id="ce2m4g"
class SingletonMeta(type):
    def __call__(cls, *args, **kwargs):
        if not hasattr(cls, "_instance"):
            cls._instance = super().__call__(*args, **kwargs)
        return cls._instance

class Config(metaclass=SingletonMeta):
    pass
```今では毎日`Config()`呼び出しは同じオブジェクトを返します。

これは慎重に使用してください。メタクラス`__call__`クラスのインスタンス作成をグローバルに変更します。

## 44.15 インスタンスの作成

通常クラスの場合:```python id="6kcvu5"
class User:
    def __new__(cls, name):
        print("__new__")
        return super().__new__(cls)

    def __init__(self, name):
        print("__init__")
        self.name = name

u = User("Ada")
```出力:```text id="868s0q"
__new__
__init__

__new__オブジェクトを作成します。__init__オブジェクトを初期化します。__new__静的なコンストラクターです。クラスを受け取り、オブジェクトを返します。__init__作成されたインスタンスを受け取り、返される必要がありますNone

44.16 クラス辞書

クラスはその名前空間を次のように公開します。__dict__。```python id="zk9rbi" class User: kind = "human"

def hello(self):
    return "hello"

print(User.dict)


`User.__dict__`通常は読み取り専用のマッピング プロキシです。```python id="pj2w2d"
print(type(User.__dict__))
```マッピング プロキシに直接割り当てることはできません。```python id="sgmmu2"
User.__dict__["x"] = 1
```ただしクラスに属性を割り当てることはできます。```python id="4utuvq"
User.x = 1
```これにより型機構を通じて基礎となるクラス辞書が更新されます

## 44.17 クラス変数

クラス変数はクラス オブジェクトに格納されます。```python id="hkk4e1"
class Counter:
    count = 0
```クラス経由でアクセスします。```python id="kts1o0"
print(Counter.count)
```インスタンスを介したアクセス:```python id="8w340h"
c = Counter()
print(c.count)
```インスタンスにない場合は、`count`、検索するとクラスで見つかります

インスタンスを介した割り当てではインスタンス属性が作成または更新されます。```python id="d1dq8x"
c.count = 10

print(c.__dict__)
print(Counter.count)
```出力:```text id="frwv5a"
{'count': 10}
0
```これは可変クラス変数に関する一般的なバグの原因です

## 44.18 可変クラス変数の落とし穴```python id="x8s3te"
class Bag:
    items = []

    def add(self, item):
        self.items.append(item)
```使用法:```python id="81dg5u"
a = Bag()
b = Bag()

a.add("x")
print(b.items)
```出力:```text id="kqxpp6"
['x']
```両方のインスタンスは同じクラスレベルのリストを共有します

正しい設計:```python id="l9yqqx"
class Bag:
    def __init__(self):
        self.items = []

    def add(self, item):
        self.items.append(item)
```共有定数または意図的な共有状態にはクラス変数を使用しますインスタンスごとの状態にはインスタンス変数を使用します

## 44.19 インスタンス辞書

通常のユーザー定義オブジェクトにはインスタンス ディクショナリがあります。```python id="8j4hxd"
class User:
    pass

u = User()
u.name = "Ada"

print(u.__dict__)
```出力:```text id="e5beij"
{'name': 'Ada'}
```属性の割り当てではデータ記述子が割り当てをインターセプトしない限り値がインスタンス ディクショナリに保存されます

これがPython オブジェクトがデフォルトで柔軟である理由です新しい属性を動的に追加できます

##44.20`__slots__`およびインスタンスのレイアウト

クラスで定義できるのは、`__slots__`通常のインスタンス辞書の代わりに固定属性スロットを使用します。```python id="ok0zm7"
class Point:
    __slots__ = ("x", "y")

    def __init__(self, x, y):
        self.x = x
        self.y = y
```のインスタンス`Point``x`そして`y`固定スロットに。```python id="3bbvk2"
p = Point(1, 2)

print(hasattr(p, "__dict__"))
```いつもの:```text id="mk5rwc"
False
```スロットはクラスに記述子を作成します。```python id="1l1jy7"
print(Point.__dict__["x"])
print(Point.__dict__["y"])
```これらの記述子は固定された記憶場所の読み取りと書き込みを行います

## 44.21 継承

クラスは 1 つ以上の基本クラスから継承できます。```python id="9jq7to"
class Animal:
    def speak(self):
        return "..."

class Dog(Animal):
    def speak(self):
        return "woof"
```サブクラスはその基本クラスを次の場所に格納します。`__bases__`。```python id="s4yqfg"
print(Dog.__bases__)
```メソッド解決順序は次の場所に保存されます。`__mro__`。```python id="zfn7t5"
print(Dog.__mro__)
```属性検索は MRO に従います

## 44.22 メソッド解決順序

対象:```python id="z3o721"
class A:
    def f(self):
        return "A"

class B(A):
    pass

class C(B):
    pass

C().f()見つけますfAMROを通じて。python id="km9ffo" print(C.__mro__) 出力形状:```text id="mvct9m" (<class 'main.C'>, <class 'main.B'>, <class 'main.A'>, <class 'object'>)


## 44.23 多重継承

Python は多重継承をサポートしています。```python id="vr4scb"
class A:
    def f(self):
        return "A"

class B:
    def f(self):
        return "B"

class C(A, B):
    pass

print(C().f())
print(C.__mro__)
```通常は、C3 MRO ルールに従い、左端のベースが優先されます。

出力:```text id="njnz6d"
A
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
```多重継承は強力ですが、クラスがメソッドと初期化パスを共有する場合は協調的な設計が必要です。

## 44.24 協同組合`super`

`super()`MROに従います。```python id="xgd0er"
class A:
    def f(self):
        return "A"

class B(A):
    def f(self):
        return "B" + super().f()

class C(A):
    def f(self):
        return "C" + super().f()

class D(B, C):
    def f(self):
        return "D" + super().f()

print(D().f())
print(D.__mro__)
```出力:```text id="f6z3iu"
DBCA

super()「親に電話する」という意味ではありません。これは、「現在のクラスの後も MRO を継続する」ことを意味します。

これは多重継承では不可欠です。

44.25 クラスデコレータ

クラス デコレータは、クラス オブジェクトの作成後にそれを受け取ります。```python id="6z5a86" def register(cls): registry[cls.name] = cls return cls

registry = {}

@register class User: pass これはおおよそ次のとおりです。python id="alod77" class User: pass

User = register(User)


##44.26`__init_subclass__`基本クラスで定義できるのは、`__init_subclass__`サブクラス化されたときにコードを実行します。```python id="zfxjod"
class Model:
    registry = []

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        Model.registry.append(cls)

class User(Model):
    pass

class Post(Model):
    pass

print(Model.registry)
```これは多くの場合、サブクラス登録用のメタクラスよりも簡単です。

使用`__init_subclass__`のために:```text id="yp6gp0"
subclass registration
subclass validation
default subclass configuration
lightweight framework hooks
```##44.27`__set_name__`クラス作成中

クラスを作成した後、CPython `__set_name__`クラス名前空間の記述子について。```python id="5vzb6d"
class Field:
    def __set_name__(self, owner, name):
        print(owner, name)

class User:
    id = Field()
    name = Field()
```出力形状:```text id="vzb7xd"
<class '__main__.User'> id
<class '__main__.User'> name
```これにより、記述子は、割り当てられているクラスと属性の名前を検出できるようになります。

順序は次のとおりです。```text id="4ow51f"
class body executes
class object is created
descriptor __set_name__ hooks run
__init_subclass__ hooks run on bases
class decorators run
class name is bound
```## 44.28 メタクラス vs クラス デコレータ vs`__init_subclass__`|メカニズム |走る |最適 |
|---|---|---|
|メタクラス |クラス作成中 |クラスオブジェクトの作成を詳細に制御 |
|クラスデコレーター |クラス作成後 | 1 回限りの変換または登録 |
|`__init_subclass__`|サブクラス作成時 |基本クラス駆動のサブクラスフック |
|ディスクリプタ`__set_name__`|クラスの終了処理中 |フィールド名の検出 |

問題を解決する最も単純なメカニズムを優先します。

ほとんどのコードの場合:```text id="wd9e73"
class decorator > __init_subclass__ > metaclass
```メタクラスは、名前空間の準備、クラス割り当て、メタクラス レベルのメソッド、クラス呼び出しをカスタマイズする必要がある場合、またはクラス階層全体にルールを適用する必要がある場合に正当化されます。

## 44.29 メタクラスの例: 必須属性の強制```python id="p13a12"
class RequireTableName(type):
    def __new__(mcls, name, bases, namespace):
        cls = super().__new__(mcls, name, bases, namespace)

        if bases and not hasattr(cls, "table_name"):
            raise TypeError(f"{name} must define table_name")

        return cls

class Model(metaclass=RequireTableName):
    pass

class User(Model):
    table_name = "users"
```このメタクラスはサブクラスを検証します。

これは次のように実装することもできます`__init_subclass__`、多くの場合、これはより単純です。```python id="8wx9md"
class Model:
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        if not hasattr(cls, "table_name"):
            raise TypeError(f"{cls.__name__} must define table_name")
```メタクラスが具体的な利点を提供しない限り、基本クラスのフックを使用してください。

## 44.30 メタクラスの例: レジストリ```python id="q07d17"
class RegistryMeta(type):
    registry = {}

    def __init__(cls, name, bases, namespace):
        super().__init__(name, bases, namespace)
        if name != "Base":
            RegistryMeta.registry[name] = cls

class Base(metaclass=RegistryMeta):
    pass

class User(Base):
    pass

class Post(Base):
    pass

print(RegistryMeta.registry)
```これにより、サブクラスが作成されたときにそのサブクラスが記録されます。

また、`__init_subclass__`十分かもしれません:```python id="byf0x9"
class Base:
    registry = {}

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        Base.registry[cls.__name__] = cls
```メタクラスは、単に強力であると感じる場合ではなく、システムを簡素化する場合に使用する必要があります。

## 44.31 メタクラスの例: カスタム名前空間

メタクラスは、カスタム名前空間を返すことによって重複した属性名を拒否できます。`__prepare__````python id="9r9h1q"
class NoDuplicateDict(dict):
    def __setitem__(self, key, value):
        if key in self:
            raise TypeError(f"duplicate name: {key}")
        super().__setitem__(key, value)

class NoDuplicateMeta(type):
    @classmethod
    def __prepare__(mcls, name, bases):
        return NoDuplicateDict()

class Example(metaclass=NoDuplicateMeta):
    x = 1
    y = 2
```もし`x`2 回割り当てられた場合、クラスの作成は失敗します。

これは、メタクラスのみが実行前にクラス本体の名前空間をカスタマイズできるため、メタクラスが適切なケースの 1 つです。

## 44.32 組み込みクラスとヒープタイプ

CPython には、静的に定義された組み込みタイプと動的に割り当てられたヒープ タイプの両方があります。

組み込み型の例:```python id="bsov46"
int
str
list
dict
tuple
type
object
```多くの組み込み型は C で定義されています。

ユーザー定義クラスは、実行時に作成されるヒープ タイプです。

どちらも同じオブジェクト モデルに参加しています。```python id="fbhr47"
print(type(list))
print(type(object))
print(type(type))
```出力:```text id="errhki"
<class 'type'>
<class 'type'>
<class 'type'>
```組み込みクラスもオブジェクトです。

##44.33`object`そして`type`間の関係`object`そして`type`中心です。```python id="ls93k5"
print(isinstance(object, type))
print(isinstance(type, object))
print(type(type))
print(type(object))
```重要な事実:```text id="9186px"
object is the base class of most Python objects
type is the default metaclass of classes
type is an instance of itself
object is an instance of type
type inherits from object
```この円形に見える構造は、CPython 内で注意深くブートストラップされています。

これにより、ルート継承階層を持ちながらクラスをオブジェクトにすることができます。

## 44.34 CPython 型オブジェクト

C レベルでは、クラス オブジェクトは通常、型オブジェクトによって表されます。`PyTypeObject`

型オブジェクトには以下が含まれます。```text id="pj1kt2"
object header
type name
basic instance size
item size for variable-sized objects
method table
slot functions
base classes
MRO
dictionary
flags
allocation functions
deallocation functions
attribute access functions
call behavior
numeric, sequence, and mapping operation tables
```これが、クラスがオブジェクトの動作を制御する理由です。クラス オブジェクトには、ランタイムによって使用される関数ポインターとメタデータが含まれています。

## 44.35 タイプ スロット

Python の特殊メソッドは、多くの場合、C レベルのスロットに対応します。

:

| Python メソッド |ランタイム操作 |
|---|---|
|`__len__`|長さの演算 |
|`__getitem__`|インデックス作成 |
|`__setitem__`|項目の割り当て |
|`__iter__`|反復 |
|`__next__`|次の反復子 |
|`__call__`|電話 |
|`__add__`|数値の足し算 |
|`__getattribute__`|属性の検索 |
|`__new__`|割り当て |
|`__init__`|初期化 |

クラスが作成されると、CPython は特別なメソッドからスロット テーブルを構築します。

これにより、バイトコードと C API は、通常の Python 属性検索を毎回実行することなく効率的に操作を呼び出すことができます。

## 44.36 特別なメソッドの検索

特別なメソッドの検索では、多くの場合、インスタンス ディクショナリがバイパスされます。

例:```python id="w8gu52"
class Example:
    pass

e = Example()
e.__len__ = lambda: 10

len(e)
```これにより、`TypeError` なぜなら`len(e)`探します`__len__`任意のインスタンス属性としてではなく、型に基づいて。

正しい:```python id="a41p2c"
class Example:
    def __len__(self):
        return 10

e = Example()
print(len(e))
```特別なメソッドがクラスに属しているため、CPython は型スロットを設定して使用できます。

## 44.37 クラスの属性検索

たとえばアクセス:```python id="ars6ez"
obj.attr
```CPython はインスタンスとそのクラス階層を検索します。

クラスアクセスの場合:```python id="e6vx8d"
Class.attr
```CPython はクラス オブジェクトとそのメタクラスを検索します。

例:```python id="511n1a"
class Meta(type):
    label = "meta"

class User(metaclass=Meta):
    label = "class"

print(User.label)
```出力:```text id="8dznft"
class
```もし`label`は欠席しています`User`、検索すると見つかるかもしれません`Meta`

メタクラスは、クラス オブジェクトの動作と属性を定義します。

## 44.38 メタクラスのメソッド

メタクラス メソッドは、クラス オブジェクトのメソッドです。```python id="7avylj"
class Meta(type):
    def describe(cls):
        return cls.__name__

class User(metaclass=Meta):
    pass

print(User.describe())
```ここ、`describe`で見つかりました`Meta`そしてそれに縛られています`User`

これは、インスタンス メソッドがどのように検索されるかに似ています。`User`そしてそれに縛られています`user_instance````text id="dm9zxe"
instance method:
    User.method -> bound to instance

metaclass method:
    Meta.method -> bound to class object
```## 44.39 クラスと記述子

クラス ディクショナリには記述子が格納されます。```python id="jcw8ig"
class User:
    @property
    def name(self):
        return "Ada"

print(User.__dict__["name"])
```インスタンスがアクセスするとき`name`、記述子ロジックが実行されます。```python id="bochjq"
u = User()
print(u.name)
```メソッドも記述子です。```python id="yrosvm"
class User:
    def hello(self):
        return "hello"

u = User()
print(u.hello)
```に格納されている関数`User.__dict__`にバインドする`u`その記述子を通じて`__get__`

## 44.40 クラスとガベージコレクション

クラス オブジェクトは参照サイクルに参加できます。

:```text id="zbbqva"
class object references methods
methods reference globals
functions reference code objects
closures may reference class-related state
instances reference class
class may reference descriptors
descriptors may reference owner class
```CPython の循環ガベージ コレクターは、他の参照によって維持されていない場合、到達不能なクラスおよび関連オブジェクトを収集できます。

ほとんどの通常のクラスは、モジュールがそれらへの参照を保持するため、プロセスが終了するまで存続します。```python id="25zzxi"
class User:
    pass
```モジュール辞書には次の内容が含まれます。`User`

## 44.41 クラスとモジュール

クラスは、それが定義されたモジュールを記録します。```python id="23z7po"
class User:
    pass

print(User.__module__)
```いつもの:```text id="btn8n6"
__main__
```またはモジュール名。

これは、表現、ピクルス、文書化、および内観に影響します。

クラスは物理的にモジュールに属しません。モジュール辞書は、クラス オブジェクトへの参照を保持するだけです。```python id="vxme1s"
OtherName = User
```同じクラス オブジェクトに別の名前が付けられるようになりました。

## 44.42 クラスアイデンティティ

クラスのアイデンティティはオブジェクトのアイデンティティです。```python id="05l12d"
class User:
    pass

A = User
B = User

print(A is B)
```出力:```text id="krnpze"
True
```モジュールを異なる名前で 2 回インポートすると、クラス オブジェクトが重複する可能性があります。```text id="59akdg"
package.models.User
models.User
```これらは、同じファイルからのものであっても、異なるクラス オブジェクトである可能性があります。

それは壊れます`isinstance`、レジストリ検索、シリアル化、およびシングルトンの仮定。

## 44.43 クラスのアノテーション

クラスのアノテーションは次の場所に保存されます。`__annotations__````python id="qmsv40"
class User:
    id: int
    name: str = "anonymous"

print(User.__annotations__)
```出力:```text id="fzxf79"
{'id': <class 'int'>, 'name': <class 'str'>}
```注釈はインスタンス フィールドを自動的に作成しません。```python id="pveplo"
u = User()
print(hasattr(u, "id"))
```いつもの:```text id="lxeohq"
False
```データクラス、attrsPydanticORM などのフレームワークは、アノテーションを検査して動作を生成します。

## 44.44 クラス変換としてのデータクラス`dataclasses.dataclass`クラスデコレータです。```python id="r4xqnl"
from dataclasses import dataclass

@dataclass
class User:
    id: int
    name: str
```クラス オブジェクトを受け取り、注釈を検査し、次のようなメソッドを追加します。```text id="i9m60v"
__init__
__repr__
__eq__
```これは、メタクラスを使用せずに、クラスの作成後に主要なクラスの動作を追加できることを示しています。

## 44.45 抽象基本クラス

`abc`モジュールはメタクラス機構を使用します。```python id="cudwvv"
from abc import ABC, abstractmethod

class Store(ABC):
    @abstractmethod
    def get(self, key):
        pass

ABC用途ABCMeta、抽象メソッドを追跡し、実装されるまでインスタンス化を防止するメタクラス。```python id="j5b315" class BadStore(Store): pass

BadStore() ```これにより、TypeError

抽象基本クラスは、クラス コントラクトを強制するメタクラスの実際的な例です。

44.46 クラスとisinstance

isinstance(obj, cls)通常は次のことを確認しますtype(obj)clsまたはのサブクラスcls。```python id="3almnz" class Animal: pass

class Dog(Animal): pass

d = Dog()

print(isinstance(d, Dog)) print(isinstance(d, Animal)) ```メタクラスはこれをカスタマイズできます__instancecheck__

ABC 機構はこれを仮想サブクラスの動作に使用します。

44.47 クラスとissubclass

issubclass(A, B)クラスかどうかを確認しますAクラスのサブクラスですB。```python id="3tkb0g" class A: pass

class B(A): pass

print(issubclass(B, A)) ```メタクラスはこれをカスタマイズできます__subclasscheck__

これは、クラスの動作がメタクラス ロジックによって仲介されるもう 1 つの場所です。

44.48 動的クラスの作成

動的クラスの作成は、フレームワークやコード生成で役立ちます。```python id="pq2ye4" def make_model(name, fields): namespace = {"annotations": fields} return type(name, (), namespace)

User = make_model("User", {"id": int, "name": str})

print(User) print(User.annotations) 動的クラスは通常のクラス規則に従う必要があります。text id="kwqh7u" valid name clear module stable identity predictable bases explicit public API セット`__module__`必要な場合:python id="hyvbmw" namespace = { "module": name, "annotations": fields, }


## 44.49 メタクラスを使用する場合

次のいずれかが必要な場合は、メタクラスを使用します。```text id="j2iimq"
custom class namespace through __prepare__
control over class allocation through __new__
metaclass-level methods or properties
custom class call behavior
deep integration across a class hierarchy
framework-level class validation
special instance or subclass checks
```クラス デコレータまたは`__init_subclass__`十分です。

メタクラスはクラス階層に対してグローバルです。無関係なライブラリが異なるメタクラスを定義している場合、それらは適切に構成されません。

## 44.50 重要なポイント

クラスはランタイム オブジェクトです。

ほとんどのクラスは次のインスタンスです。`type`

メタクラスはクラスの型です。

クラス定義は、本体を実行し、名前空間を収集し、メタクラスを呼び出してクラス オブジェクトを作成します。

インスタンスは、メタクラスによって処理されるクラスを呼び出すことによって作成されます。`__call__`

クラス ディクショナリには、メソッド、記述子、注釈、およびクラス変数が格納されます。

継承ではメソッド解決順序が使用されます。

特別なメソッドは型スロットにインストールされ、通常はインスタンスではなくクラスで検索されます。

記述子、`__set_name__``__init_subclass__`、クラス デコレータ、およびメタクラスはすべてクラスの構築に参加します。

メタクラスは、より単純なクラス メカニズムでは必要な動作を表現できない場合にのみ使用してください。