Словарь по сравнению с Объектом - который более эффективен и почему?

Попробуйте -XX:MaxPermSize=256m и если это сохраняется, попробуйте -XX:MaxPermSize=512m

118
задан tkokoszka 26 August 2009 в 18:55
поделиться

5 ответов

Вы пробовали использовать __ slots __ ?

Из документация :

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

Значение по умолчанию можно изменить, указав __ slots __ в определении класса нового стиля. Объявление __ slots __ принимает последовательность переменных экземпляра и резервирует достаточно места в каждом экземпляре для хранения значения для каждой переменной. Место экономится, потому что __ dict __ не создается для каждого экземпляра.

Так это экономит время, а также память?

Сравнение трех подходов на моем компьютере:

test_slots.py:

class Obj(object):
  __slots__ = ('i', 'l')
  def __init__(self, i):
    self.i = i
    self.l = []
all = {}
for i in range(1000000):
  all[i] = Obj(i)

test_obj.py:

class Obj(object):
  def __init__(self, i):
    self.i = i
    self.l = []
all = {}
for i in range(1000000):
  all[i] = Obj(i)

test_dict.py:

all = {}
for i in range(1000000):
  o = {}
  o['i'] = i
  o['l'] = []
  all[i] = o

test_ namedtuple.py (поддерживается в 2.6):

import collections

Obj = collections.namedtuple('Obj', 'i l')

all = {}
for i in range(1000000):
  all[i] = Obj(i, [])

Выполнить тест (с использованием CPython 2.5):

$ lshw | grep product | head -n 1
          product: Intel(R) Pentium(R) M processor 1.60GHz
$ python --version
Python 2.5
$ time python test_obj.py && time python test_dict.py && time python test_slots.py 

real    0m27.398s (using 'normal' object)
real    0m16.747s (using __dict__)
real    0m11.777s (using __slots__)

Использование CPython 2.6.2, включая тест именованного кортежа :

$ python --version
Python 2.6.2
$ time python test_obj.py && time python test_dict.py && time python test_slots.py && time python test_namedtuple.py 

real    0m27.197s (using 'normal' object)
real    0m17.657s (using __dict__)
real    0m12.249s (using __slots__)
real    0m12.262s (using namedtuple)

Так что да (не удивительно), использование __ slots __ - это оптимизация производительности.

148
ответ дан 24 November 2019 в 01:59
поделиться

Доступ к атрибутам в объекте использует скрытый доступ к словарю, поэтому, используя доступ к атрибутам, вы добавляете дополнительные накладные расходы. Кроме того, в случае с объектами вы несете дополнительные накладные расходы, например, из-за выделения дополнительной памяти и выполнения кода (например, метода __ init __ ).

В вашем коде, если o равно экземпляр Obj , o.attr эквивалентен o .__ dict __ ['attr'] с небольшими дополнительными накладными расходами.

14
ответ дан 24 November 2019 в 01:59
поделиться

Рассматривали ли вы использование namedtuple ? ( ссылка для python 2.4 / 2.5 )

Это новый стандартный способ представления структурированных данных, который дает вам производительность кортежа и удобство класса.

Это только обратная сторона по сравнению со словарями в том, что (как и кортежи) он не дает вам возможности изменять атрибуты после создания.

8
ответ дан 24 November 2019 в 01:59
поделиться
from datetime import datetime

ITER_COUNT = 1000 * 1000

def timeit(method):
    def timed(*args, **kw):
        s = datetime.now()
        result = method(*args, **kw)
        e = datetime.now()

        print method.__name__, '(%r, %r)' % (args, kw), e - s
        return result
    return timed

class Obj(object):
    def __init__(self, i):
       self.i = i
       self.l = []

class SlotObj(object):
    __slots__ = ('i', 'l')
    def __init__(self, i):
       self.i = i
       self.l = []

@timeit
def profile_dict_of_dict():
    return dict((i, {'i': i, 'l': []}) for i in xrange(ITER_COUNT))

@timeit
def profile_list_of_dict():
    return [{'i': i, 'l': []} for i in xrange(ITER_COUNT)]

@timeit
def profile_dict_of_obj():
    return dict((i, Obj(i)) for i in xrange(ITER_COUNT))

@timeit
def profile_list_of_obj():
    return [Obj(i) for i in xrange(ITER_COUNT)]

@timeit
def profile_dict_of_slotobj():
    return dict((i, SlotObj(i)) for i in xrange(ITER_COUNT))

@timeit
def profile_list_of_slotobj():
    return [SlotObj(i) for i in xrange(ITER_COUNT)]

if __name__ == '__main__':
    profile_dict_of_dict()
    profile_list_of_dict()
    profile_dict_of_obj()
    profile_list_of_obj()
    profile_dict_of_slotobj()
    profile_list_of_slotobj()

Результаты:

hbrown@hbrown-lpt:~$ python ~/Dropbox/src/StackOverflow/1336791.py 
profile_dict_of_dict ((), {}) 0:00:08.228094
profile_list_of_dict ((), {}) 0:00:06.040870
profile_dict_of_obj ((), {}) 0:00:11.481681
profile_list_of_obj ((), {}) 0:00:10.893125
profile_dict_of_slotobj ((), {}) 0:00:06.381897
profile_list_of_slotobj ((), {}) 0:00:05.860749
4
ответ дан 24 November 2019 в 01:59
поделиться

Нет вопросов.
У вас есть данные без других атрибутов (без методов, ничего). Следовательно, у вас есть контейнер данных (в данном случае словарь).

Я обычно предпочитаю думать в терминах моделирования данных . Если возникает серьезная проблема с производительностью, я могу отказаться от чего-то в абстракции, но только по очень веским причинам.
Программирование - это управление сложностью, и поддержание правильной абстракции очень часто является одним из наиболее полезных способов достижения такого результата.

О причинах объект работает медленнее, Я считаю, что ваши измерения неверны.
Вы выполняете слишком мало присваиваний внутри цикла for, и поэтому вы видите разное время, необходимое для создания экземпляра dict (внутреннего объекта) и «настраиваемого» объекта. Хотя с точки зрения языка они одинаковы, но имеют совершенно разную реализацию.
После этого время назначения должно быть почти одинаковым для обоих, так как конечные члены поддерживаются в словаре.

2
ответ дан 24 November 2019 в 01:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: