- en
- Language: ru
- Documentation version: latest
10. __slots___Магия¶
В Python каждый класс может иметь атрибуты экземпляра. По умолчанию Python использует dict для хранения атрибутов экземпляра объекта. Это очень удобно, так как позволяет задавать произвольные новые атрибуты во время выполнения.
Однако для небольших классов с известными атрибутами это может оказаться узким местом. dict
тратит много оперативной памяти. Python не может просто выделить статический объем памяти при создании объекта для хранения всех атрибутов. Поэтому он отсасывает много оперативной памяти, если вы создаете много объектов (я говорю о тысячах и миллионах). Тем не менее, есть способ обойти эту проблему. Он заключается в использовании __slots__
, чтобы указать Python не использовать dict, а выделять место только для фиксированного набора атрибутов. Вот пример с и без __slots__
:
Без __slots__
:
class MyClass(object):
def __init__(self, name, identifier):
self.name = name
self.identifier = identifier
self.set_up()
# ...
With __slots__
:
class MyClass(object):
__slots__ = ['name', 'identifier']
def __init__(self, name, identifier):
self.name = name
self.identifier = identifier
self.set_up()
# ...
Второй фрагмент кода снизит нагрузку на оперативную память. Некоторые люди наблюдали снижение использования оперативной памяти почти на 40-50% благодаря использованию этой техники.
В качестве примечания, вы можете попробовать PyPy. Он выполняет все эти оптимизации по умолчанию.
Ниже приведен пример, показывающий точное использование памяти с и без __slots__
, выполненный в IPython благодаря https://github.com/ianozsvald/ipython_memory_usage.
Python 3.4.3 (default, Jun 6 2015, 13:32:34)
Type "copyright", "credits" or "license" for more information.
IPython 4.0.0 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object', use 'object??' for extra details.
In [1]: import ipython_memory_usage.ipython_memory_usage as imu
In [2]: imu.start_watching_memory()
In [2] used 0.0000 MiB RAM in 5.31s, peaked 0.00 MiB above current, total RAM usage 15.57 MiB
In [3]: %cat slots.py
class MyClass(object):
__slots__ = ['name', 'identifier']
def __init__(self, name, identifier):
self.name = name
self.identifier = identifier
num = 1024*256
x = [MyClass(1,1) for i in range(num)]
In [3] used 0.2305 MiB RAM in 0.12s, peaked 0.00 MiB above current, total RAM usage 15.80 MiB
In [4]: from slots import *
In [4] used 9.3008 MiB RAM in 0.72s, peaked 0.00 MiB above current, total RAM usage 25.10 MiB
In [5]: %cat noslots.py
class MyClass(object):
def __init__(self, name, identifier):
self.name = name
self.identifier = identifier
num = 1024*256
x = [MyClass(1,1) for i in range(num)]
In [5] used 0.1758 MiB RAM in 0.12s, peaked 0.00 MiB above current, total RAM usage 25.28 MiB
In [6]: from noslots import *
In [6] used 22.6680 MiB RAM in 0.80s, peaked 0.00 MiB above current, total RAM usage 47.95 MiB