Python – slots でクラスのメモリを削減する

Python は柔軟性が高く、簡易なコーディングで幅広い操作を行うことができます。
その反面、メモリ消費が比較的多いデメリットがあります。

メモリ賞を改善するためのテクニックとして、__slots__ を利用した手法があります。

目次

__slots__ を利用しない場合のメモリ量

まず比較のため、__slots__ を利用しないコードのメモリ量を計測します。

class SampleClass:
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

instances = [SampleClass(1, 2, 3) for i in range(10_000_000)]
$ /usr/bin/time -f "Elapsed: %E\nMemory : %M [KB]" python3 main.py 
Elapsed: 0:09.39
Memory : 1654656 [KB]

引数を3つ保持する SampleClass を 1000万だけリストに追加します。
生成には 10秒ほどを要し、1.65 GB のメモリが消費されています。

__slots__ を利用する場合

__slots__ の定義をすると、次のようになります。

class SampleClass:
    __slots__ = ("a", "b", "c")

    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

instances = [SampleClass(1, 2, 3) for i in range(10_000_000)]
$ /usr/bin/time -f "Elapsed: %E\nMemory : %M [KB]" python3 main_slots.py 
Elapsed: 0:07.15
Memory : 713384 [KB]

__slots__ を指定することで、メモリの消費が 713 [MB] に軽減されています。
副次効果として、時間も 7秒ほどに軽減されています。

解説:__slots__ とは

Python クラスの __dict__ 属性

Python のクラスは、__dict__ という属性を暗黙的に保持します。
これはインスタンスが保持するメンバを、ディクショナリ形式で持っているものになります。

この恩恵として、クラスインスタンスに新しいメンバを動的に割り当てることができるようになっています。

class SampleClass:
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

instance = SampleClass(1, 2, 3)
print(instance.__dict__)
# {'a': 1, 'b': 2, 'c': 3}

instance.d = 4  # 新規メンバを動的に割り当てられる
print(instance.__dict__)
# {'a': 1, 'b': 2, 'c': 3, 'd': 4}

__slots__ でメンバを厳密定義する

__slots__ には、クラスのメンバの一覧を文字列で定義します。

__slots__ を定義することで、クラスインスタンスが __dict__ 属性を保持しないようになります。
これによって、クラスの柔軟性が失われる代わりに __dict__ に消費されるメモリを削減することができます。

コードの例として、次のように __dict__ アクセスや新規メンバの割り当てでエラーが発生するようになります。

class SampleClass:
    __slots__ = ("a", "b", "c")

    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

instance = SampleClass(1, 2, 3)

print(instance.__dict__)
# AttributeError: 'SampleClass' object has no attribute '__dict__'. Did you mean: '__dir__'?

instance.d = 4
# AttributeError: 'SampleClass' object has no attribute 'd'

dataclass での __slots__ 定義

Python 3.10 以降であれば、dataclass でも __slots__ を有効にすることができます。

from dataclasses import dataclass

@dataclass(slots=True)
class SampleClass:
    a: int
    b: int
    c: int

instances = [SampleClass(1, 2, 3) for i in range(10_000_000)]
$ /usr/bin/time -f "Elapsed: %E\nMemory : %M [KB]" python3 main_slots_dataclass.py 
Elapsed: 0:06.85
Memory : 717840 [KB]
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次