?

Log in

No account? Create an account
nyaload

Журнал Пушыстого

Журнал Пушыстого

Previous Entry Share Next Entry
python namedtuple memory
nyaload
_winnie
В питоне есть namedtuple, тупл к полям которого можно обращаться привычным синтаксисом object.field

namedtuple лучше обычных туплов тем, что к полям тупла можно обращаться по имени, легко добавлять к типу новые поля и удалять.
namedtuple лучше словарей/объектов тем, что не расходуют память на имена полей в каждом объекте.

Использование такое:


from collections import namedtuple
Point = namedtuple('Point', ['x', 'y']) # Point будет типом, как dict или int
p = Point(1,2)
print p.x, p.y


Померял сколько памяти занимает миллион пар-туплов, с несколькими вариантами - словари, объекты, обычные туплы.

64-битный python 2.7, память смотрел в утилите top (RES), примитивным кодом - https://gist.github.com/dobrokot/7416c65c557fd8260176

  8m - 32-bit числа, минимальный размер массива 
 21m - 32-bit числа, реальное потребление на numpy-массив + оверхед на интерпретатор
 16m - 64-bit числа, минимальный размер массива 
 28m - 64-bit числа, реальное потребление на numpy-массив + оверхед на интерпретатор
 13m - запуск интерпретатора с import numpy
 67m - two arrays 
130m - tuple
139m - namedtuple
334m - dict
405m - class


Вывод: namedtuple - хороший выбор по умолчанию для массивов однотипных объектов, если отказываться от синтаксиса object.field и от питона не хочется, а привязка к размеру и порядку полей в тупле вызывает тошноту и проблемы поддержки программы.

  • 1
Ещё есть __slots__. А c 3.4 и в обычном классе память на имена полей в каждом объекте не расходуется, если поля проинициализированы в конструкторе. Это, конечно, не так экономно, как namedtuple, зато в довесок не идёт паразитный протокол.

Самый же экономный способ — запаковать поля в целое.

class Point(int): # long в Python 2
    def __new__(cls, x, y):
        assert 0 <= x < 2**32
        assert 0 <= y < 2**32
        return int.__new__(cls, (x << 32) + y)
    @property
    def x(self):
        return self >> 32
    @property
    def y(self):
        return self & (2**32 - 1)
    def __repr__(self):
        return '%s(%r, %r)' % (self.__class__.__name__, self.x, self.y)
    __str__ = __repr__

о, удобный способ. и в качестве быстрого использования в интерпретаторе годится.

Век живи, век учись.
А я раньше велосипедил примерно так
class AdHoc:
	def __init__(self, **kwargs):
		self.__dict__.update(kwargs)
	def __str__(self):
		return repr(self)
	def __repr__(self):
		return 'AdHoc(%s)' % (
			', '.join(
				'%s=%r' % kv for kv in self.__dict__.items()
				)
			)

p = AdHoc(x=1, y=2, z='hello')
print(p.x, p.y, p.z) # 1 2 hello
print(p)             # AdHoc(x=1, y=2, z='hello') -- порядок может не сохраниться, кстати
p.t = None           # атрибуты можно добавлять по ходу пьесы
print(p)             # AdHoc(x=1, y=2, z='hello', t=None)
p.t = p
print(p)             # исчерпание стека :)))
del p.t              # удалять атрибуты тоже можно
print(p)             # AdHoc(x=1, y=2, z='hello')


Но вот идея генерировать класс с заданными полями - это симпатично.

Edited at 2015-01-14 09:49 pm (UTC)

  • 1