0Pricing
Python Academy · レッスン

__slots__ とメモリ最適化

__slots__ を使ってインスタンスごとのメモリー使用量を削減します。

「__slots__ とメモリ最適化」はCoddyKit上の無料Python Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはPython Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Python Academyコースには全4レッスンが含まれています。

デフォルトの __dict__ のオーバーヘッド

デフォルトでは、すべてのPythonインスタンスが属性を __dict__ ディクショナリに保存します。これには大きなメモリオーバーヘッドがあり、通常はインスタンスごとに200~400バイトを消費します。

import sys

class Point:
    def __init__(self, x, y): self.x, self.y = x, y

p = Point(1, 2)
print(sys.getsizeof(p.__dict__))   # ~200 bytes

__slots__ の宣言

__slots__ を属性名のシーケンスとして定義します。Pythonはディクショナリの代わりに固定サイズの配列を割り当てるため、メモリを節約できます。

class SlottedPoint:
    __slots__ = ("x", "y")

    def __init__(self, x, y):
        self.x = x
        self.y = y

p = SlottedPoint(1, 2)
print(p.x, p.y)   # 1 2
# p.z = 3   # AttributeError: no __dict__

メモリの節約

__slots__ を使うと、各インスタンスのメモリ使用量を、ディクショナリベースのインスタンスと比べて通常30~50%削減できます。数百万個のオブジェクトを作成する場合には、大きな効果があります。

import sys

class Reg:
    def __init__(self, x, y): self.x, self.y = x, y

class Slotted:
    __slots__ = ("x","y")
    def __init__(self, x, y): self.x, self.y = x, y

print(sys.getsizeof(Reg(1,2)))      # ~56 bytes + ~200 dict
print(sys.getsizeof(Slotted(1,2)))  # ~56 bytes (no dict)

デフォルトでは __dict__ を持たない

スロットを使用するクラスには __dict__ がないため、作成後に任意の属性を追加することはできません。

class Config:
    __slots__ = ("host", "port")
    def __init__(self, h, p): self.host, self.port = h, p

c = Config("localhost", 8080)
# c.debug = True   # AttributeError
print(hasattr(c, "__dict__"))   # False

__slots__ で __dict__ を保持する

__slots__ に "__dict__" を含めると、共通の属性をスロットとして事前に宣言しながら、インスタンスごとのディクショナリも保持できます。

class Hybrid:
    __slots__ = ("x", "__dict__")

    def __init__(self, x):
        self.x = x

h = Hybrid(1)
h.extra = "dynamic"   # allowed
print(h.extra)

__slots__ の __weakref__

スロットを使用するクラスでは、弱参照のサポートが失われます。__slots__ に "__weakref__" を追加すると、弱参照を再び有効にできます。

import weakref

class Node:
    __slots__ = ("value", "__weakref__")
    def __init__(self, v): self.value = v

n = Node(42)
ref = weakref.ref(n)
print(ref())  # <Node object>

継承と __slots__

親クラスが __slots__ を使用していない場合、子クラスには __dict__ が引き継がれます。メモリを完全に節約するには、継承階層全体で __slots__ を定義する必要があります。

class Base:
    __slots__ = ("x",)

class Child(Base):
    __slots__ = ("y",)   # no __dict__

class WithoutSlots(Base):
    pass   # gets __dict__ from object

メモリのベンチマーク

__slots__ を追加する前後で実際のメモリ使用量を測定するには、tracemalloc または pympler を使用します。

import tracemalloc

tracemalloc.start()
points = [SlottedPoint(i, i) for i in range(100_000)]
current, peak = tracemalloc.get_traced_memory()
tracemalloc.stop()
print(f"Peak: {peak/1024/1024:.1f} MB")

__slots__ と Pickle化

スロットを使用するオブジェクトは、__getstate__ と __setstate__ を定義するか、Python 3以降でスロットを自動的に処理するデフォルトのpickleプロトコルを使用すれば、pickle化できます。

import pickle

class Point:
    __slots__ = ("x", "y")
    def __init__(self, x, y): self.x, self.y = x, y

p = Point(3, 4)
data = pickle.dumps(p)
p2 = pickle.loads(data)
print(p2.x, p2.y)   # 3 4

__slots__ を持つ dataclasses — Python 3.10以降

@dataclass に slots=True を渡すと、__slots__ が自動的に生成されます。

from dataclasses import dataclass

@dataclass(slots=True)
class Vector:
    x: float
    y: float

v = Vector(1.0, 2.0)
print(v.x, v.y)   # 1.0 2.0
print(hasattr(v, "__dict__"))   # False

__slots__ を使う場面

インスタンスを数百万個作成する場合、メモリに制約がある場合、または属性アクセスの速度が重要な場合に __slots__ を使用してください。柔軟性のほうが重要な汎用クラスでは、使用しないでください。

# Good candidates:
# - Nodes in a large graph or tree
# - Records in a large dataset
# - High-frequency event objects

# Bad candidates:
# - Configuration objects added to ad-hoc
# - Classes that mix-in dynamic attributes

確認問題

スロットを使用するインスタンスで、__slots__ に記載されていない属性を設定しようとすると、どうなりますか。

まとめ

__slots__ は、インスタンスごとの __dict__ を固定サイズの配列に置き換え、メモリを30~50%節約します。"__dict__" または "__weakref__" が必要な場合は、それらを含めてください。メモリを完全に節約するには、継承チェーン全体で __slots__ を使用する必要があります。Python 3.10以降では @dataclass(slots=True) を使用してください。

よくある質問

「__slots__ とメモリ最適化」レッスンは無料ですか?

はい。「__slots__ とメモリ最適化」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Python Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Python Academyコースには全4レッスンが含まれています。

「__slots__ とメモリ最適化」で何を学びますか?

__slots__ を使ってインスタンスごとのメモリー使用量を削減します。 ブラウザで直接実行するハンズオンコードでPython Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Python Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのPython Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「__slots__ とメモリ最適化」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このPython Academyレッスンでコードを書いて実行できますか?

はい。すべてのPython Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Python のクラスが作成される仕組み
  2. カスタムメタクラスの作成
  3. ディスクリプター: __get__、__set__、__delete__
  4. __slots__ とメモリ最適化
← Python Academyに戻る