__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フィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- Python のクラスが作成される仕組み
- カスタムメタクラスの作成
- ディスクリプター: __get__、__set__、__delete__
- __slots__ とメモリ最適化