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

какова цель __slots__ в Python-особенно в отношении того, когда я хотел бы использовать его, а когда нет?

11 ответов


в Python, какова цель __slots__ а каких случаев следует избегать?

TLDR:

специальный атрибут __slots__ позволяет явно указать, какие атрибуты экземпляра вы ожидаете, что ваши экземпляры объекта будут иметь, с ожидаемыми результатами:

  1. быстрее доступ к атрибуту.
  2. экономия пространства в памяти.

экономия пространства от

  1. хранение ссылок на значения в слотах вместо __dict__.
  2. отрицание __dict__ и __weakref__ создание, если родительские классы отрицают их, и вы объявляете __slots__.

Быстрая Предостережения

небольшое предостережение, вы должны только объявить определенный слот один раз в дереве наследования. Например:

class Base:
    __slots__ = 'foo', 'bar'

class Right(Base):
    __slots__ = 'baz', 

class Wrong(Base):
    __slots__ = 'foo', 'bar', 'baz'        # redundant foo and bar

Python не возражает, когда вы получаете это неправильно (вероятно, должно), проблемы могут не проявляться иначе, но ваши объекты будут занимать больше места, чем они должны.

>>> from sys import getsizeof
>>> getsizeof(Right()), getsizeof(Wrong())
(64, 80)

самое большое предостережение для множественного наследования-несколько "родительских классов с непустыми слотами" не могут быть объединены.

чтобы учесть это ограничение, следуйте лучшим практикам: разложите на все, кроме одной или всех абстракций родителей, которые их конкретный класс соответственно и ваш новый конкретный класс коллективно наследование от-предоставление пустых слотов абстракции(s) (так же, как абстрактные базовые классы в стандартной библиотеке).

см. раздел о множественном наследовании ниже для примера.

требования:

  • чтобы атрибуты были названы в __slots__ фактически храниться в слотах вместо __dict__, класс должен наследовать от object.

  • чтобы предотвратить создание __dict__, вы должны наследуйте от object и все классы в наследство, должны заявить __slots__ и никто из них не может иметь '__dict__' запись.

есть много деталей, если вы хотите сохранить значение.

зачем использовать __slots__: более быстрый доступ к атрибуту.

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

тривиально демонстрировать значительно более быстрый доступ:

import timeit

class Foo(object): __slots__ = 'foo',

class Bar(object): pass

slotted = Foo()
not_slotted = Bar()

def get_set_delete_fn(obj):
    def get_set_delete():
        obj.foo = 'foo'
        obj.foo
        del obj.foo
    return get_set_delete

и

>>> min(timeit.repeat(get_set_delete_fn(slotted)))
0.2846834529991611
>>> min(timeit.repeat(get_set_delete_fn(not_slotted)))
0.3664822799983085

щелевой доступ почти на 30% быстрее в Python 3.5 на Ubuntu.

>>> 0.3664822799983085 / 0.2846834529991611
1.2873325658284342

в Python 2 на Windows я измерил его примерно на 15% быстрее.

зачем использовать __slots__: Экономия Памяти

еще одна цель __slots__ - уменьшить пространство в памяти, которое занимает каждый экземпляр объекта.

мой собственный вклад в в документации четко указаны причины этого:

пространство, сохраненное с помощью __dict__ может быть значительным.

атрибуты SQLAlchemy много экономии памяти в __slots__.

чтобы проверить это, используя дистрибутив Anaconda Python 2.7 на Ubuntu Linux, с guppy.hpy (он же хипи) и sys.getsizeof, размер экземпляра класса без __slots__ объявлено, и ничего больше, 64 байта. Это делает не включить __dict__. Спасибо Python за ленивую оценку еще раз,__dict__ по-видимому, не вызывается до тех пор, пока на него не ссылаются, но классы без данных обычно бесполезны. Когда вызывается к существованию,__dict__ атрибут-это минимум 280 байт дополнительно.

напротив, экземпляр класса с __slots__ объявлен () (без данных) составляет всего 16 байт и 56 байт с одним элементом в слотах, 64 с два.

для 64-битного Python я иллюстрирую потребление памяти в байтах в Python 2.7 и 3.6, для __slots__ и __dict__ (слоты не определены) для каждой точки, где дикт растет в 3.6 (за исключением 0, 1 и 2 атрибутов):

       Python 2.7             Python 3.6
attrs  __slots__  __dict__*   __slots__  __dict__* | *(no slots defined)
none   16         56 + 272†   16         56 + 112† | †if __dict__ referenced
one    48         56 + 272    48         56 + 112
two    56         56 + 272    56         56 + 112
six    88         56 + 1040   88         56 + 152
11     128        56 + 1040   128        56 + 240
22     216        56 + 3344   216        56 + 408     
43     384        56 + 3344   384        56 + 752

Итак, несмотря на меньшие дикты в Python 3, мы видим, как красиво __slots__ масштаб для экземпляров, чтобы сохранить нам память, и это основная причина, по которой вы хотели бы использовать __slots__.

просто для полноты моих заметок, обратите внимание, что существует единовременная стоимость за слот в пространстве имен класса 64 байта в Python 2 и 72 байта в Python 3, потому что слоты используют дескрипторы данных, такие как свойства, называемые "членами".

>>> Foo.foo
<member 'foo' of 'Foo' objects>
>>> type(Foo.foo)
<class 'member_descriptor'>
>>> getsizeof(Foo.foo)
72

демонстрация __slots__:

отрицать создание __dict__, вы должны подкласс object:

class Base(object): 
    __slots__ = ()

теперь:

>>> b = Base()
>>> b.a = 'a'
Traceback (most recent call last):
  File "<pyshell#38>", line 1, in <module>
    b.a = 'a'
AttributeError: 'Base' object has no attribute 'a'

или подкласс другой класс, который определяет __slots__

class Child(Base):
    __slots__ = ('a',)

и теперь:

c = Child()
c.a = 'a'

но:

>>> c.b = 'b'
Traceback (most recent call last):
  File "<pyshell#42>", line 1, in <module>
    c.b = 'b'
AttributeError: 'Child' object has no attribute 'b'

разрешить __dict__ создание при подклассе щелевых объектов просто добавьте '__dict__' до __slots__ (обратите внимание, что слоты упорядочены, и вы не должны повторять слоты, которые уже находятся в родительских классах):

class SlottedWithDict(Child): 
    __slots__ = ('__dict__', 'b')

swd = SlottedWithDict()
swd.a = 'a'
swd.b = 'b'
swd.c = 'c'

и

>>> swd.__dict__
{'c': 'c'}

или вам даже не нужно объявлять __slots__ в вашем подклассе, и вы все равно будете использовать слоты от родителей, но не ограничивать создание __dict__:

class NoSlots(Child): pass
ns = NoSlots()
ns.a = 'a'
ns.b = 'b'

и:

>>> ns.__dict__
{'b': 'b'}
, __slots__ может вызвать проблемы для множественного наследования:
class BaseA(object): 
    __slots__ = ('a',)

class BaseB(object): 
    __slots__ = ('b',)

потому что создание дочернего класса от родителей с обеих непустых слотов не удается:

>>> class Child(BaseA, BaseB): __slots__ = ()
Traceback (most recent call last):
  File "<pyshell#68>", line 1, in <module>
    class Child(BaseA, BaseB): __slots__ = ()
TypeError: Error when calling the metaclass bases
    multiple bases have instance lay-out conflict

если вы столкнетесь с этой проблемой, вы мог бы просто удалить __slots__ от родителей, или если у вас есть контроль над родителями, дайте им пустые слоты или рефакторинг абстракции:

from abc import ABC

class AbstractA(ABC):
    __slots__ = ()

class BaseA(AbstractA): 
    __slots__ = ('a',)

class AbstractB(ABC):
    __slots__ = ()

class BaseB(AbstractB): 
    __slots__ = ('b',)

class Child(AbstractA, AbstractB): 
    __slots__ = ('a', 'b')

c = Child() # no problem!

добавить '__dict__' to __slots__ чтобы получить динамическое назначение:

class Foo(object):
    __slots__ = 'bar', 'baz', '__dict__'

и так:

>>> foo = Foo()
>>> foo.boink = 'boink'

поэтому с '__dict__' в слотах мы теряем некоторые преимущества размера с преимуществом динамического назначения и все еще имеем слоты для имен, которые мы ожидаем.

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

избежать __slots__ потому что вы хотите иметь возможность добавлять атрибуты на лету на самом деле не является хорошей причиной-просто добавьте "__dict__" на __slots__ если это требуется.

вы можете аналогично добавить __weakref__ to __slots__ явно, Если вам нужна эта функция.

установить в пустой кортеж при подклассе namedtuple:

namedtuple builtin делает неизменяемые экземпляры, которые очень легкие (по сути, размер кортежей), но чтобы получить преимущества, вам нужно сделать это самостоятельно, если вы их подкласс:

from collections import namedtuple
class MyNT(namedtuple('MyNT', 'bar baz')):
    """MyNT is an immutable and lightweight object"""
    __slots__ = ()

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

>>> nt = MyNT('bar', 'baz')
>>> nt.bar
'bar'
>>> nt.baz
'baz'

и попытка назначить неожиданный атрибут вызывает AttributeError потому что мы предотвратили создание __dict__:

>>> nt.quux = 'quux'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'MyNT' object has no attribute 'quux'

вы can разрешить __dict__ создание, оставив от __slots__ = (), но вы не можете использовать непустой __slots__ С подтипы кортежа.

самое большое предостережение: множественное наследование

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

class Foo(object): 
    __slots__ = 'foo', 'bar'
class Bar(object):
    __slots__ = 'foo', 'bar' # alas, would work if empty, i.e. ()

>>> class Baz(Foo, Bar): pass
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
    multiple bases have instance lay-out conflict

пустой __slots__ в родителе, кажется, обеспечивают максимальную гибкость,позволяет ребенку выбрать, чтобы предотвратить или разрешить (путем добавления '__dict__' чтобы получить динамическое назначение, см. выше раздел) создание __dict__:

class Foo(object): __slots__ = ()
class Bar(object): __slots__ = ()
class Baz(Foo, Bar): __slots__ = ('foo', 'bar')
b = Baz()
b.foo, b.bar = 'foo', 'bar'

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

рисковать здесь: если вы пишете mixins или через абстрактные базовые классы, которые не предназначены для создания экземпляра, пустой __slots__ в этих родителях, кажется, лучший способ пойти с точки зрения гибкости для подклассификаторы.

чтобы продемонстрировать, во-первых, давайте создадим класс с кодом, который мы хотели бы использовать при множественном наследовании

class AbstractBase:
    __slots__ = ()
    def __init__(self, a, b):
        self.a = a
        self.b = b
    def __repr__(self):
        return f'{type(self).__name__}({repr(self.a)}, {repr(self.b)})'

мы могли бы использовать выше непосредственно путем наследования и объявления ожидаемых слотов:

class Foo(AbstractBase):
    __slots__ = 'a', 'b'

но нас это не волнует, это тривиальное одиночное наследование, нам нужен другой класс, от которого мы также можем наследовать, возможно, с шумным атрибутом:

class AbstractBaseC:
    __slots__ = ()
    @property
    def c(self):
        print('getting c!')
        return self._c
    @c.setter
    def c(self, arg):
        print('setting c!')
        self._c = arg

теперь, если обе базы имели непустые слоты, мы не смог сделать внизу. (На самом деле, если бы мы хотели, мы могли бы дать AbstractBase непустые слоты a и b и оставили их из приведенной ниже декларации-оставить их было бы неправильно):

class Concretion(AbstractBase, AbstractBaseC):
    __slots__ = 'a b _c'.split()

и теперь мы имеем функциональность от обоих через множественное наследование, и можем все еще отрицать __dict__ и __weakref__ создание экземпляра:

>>> c = Concretion('a', 'b')
>>> c.c = c
setting c!
>>> c.c
getting c!
Concretion('a', 'b')
>>> c.d = 'd'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Concretion' object has no attribute 'd'

другие случаи, чтобы избежать слотов:

  • избежать их, когда вы хотите выполнить __class__ назначение с другой класс, у которого их нет (и вы не можете их добавить), если макеты слотов не идентичны. (Мне очень интересно узнать, кто это делает и почему.)
  • избегайте их, если вы хотите подклассы переменной длины, такие как Long, tuple или str, и вы хотите добавить к ним атрибуты.
  • избегайте их, если вы настаиваете на предоставлении значений по умолчанию с помощью атрибутов класса, например переменных.

вы можете быть в состоянии дразнить дальнейшие предостережения от остальных __slots__ документация (документы 3.7 dev являются самыми последними), в который я недавно внес значительный вклад.

критика других ответов

текущие верхние ответы ссылаются на устаревшую информацию и довольно волнистые и пропускают отметку некоторыми важными способами.

не "использовать только __slots__ при создании большого числа объектов"

цитирую:

"вы хотел бы использовать __slots__ если вы собираетесь создать много (сотни, тысячи) объектов того же класса."

абстрактные базовые классы, например, из collections модуль, не создаются экземпляры, пока __slots__ объявлены за них.

почему?

если пользователь желает запретить __dict__ или __weakref__ создание, эти вещи не должны быть доступны в родительские классы.

__slots__ способствует повторному использованию при создании интерфейсов или миксины.

это правда, что многие пользователи Python не пишут для повторного использования, но когда вы, имея возможность запретить ненужное использование пространства является ценным.

__slots__ не ломает маринование

при мариновании щелевого объекта вы можете обнаружить, что он жалуется на вводящее в заблуждение TypeError:

>>> pickle.loads(pickle.dumps(f))
TypeError: a class that defines __slots__ without defining __getstate__ cannot be pickled

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


цитирую Джейкоб Халлен:

правильное использование __slots__ для экономии места в объектах. Вместо того динамический дикт, позволяющий добавлять атрибуты к объектам в любое время, существует статическая структура, которая не допускает дополнений после создания. [Использование __slots__ исключает накладные расходы одного дикт для каждого объекта.] Хотя это иногда полезная оптимизация, она была бы полностью ненужно, если интерпретатор Python был динамическим достаточно, чтобы это требуется только дикт, когда на самом деле были дополнения к объекту.

к сожалению, есть побочный эффект в слоты. Они изменяют поведение объекты, которые имеют слоты таким образом, что могут стать жертвой нелюдей и статические печатающие сосиски. Это плохо, потому что на контроле стоит злоупотреблять metaclasses и статические набрав weenies должны злоупотреблять декораторы, так как в Python должен быть только один очевидный способ делать что-то.

сделать CPython достаточно умным, чтобы справиться с экономией места без __slots__ является крупным предпринимая, что, вероятно, поэтому его нет в списке изменений для P3k (пока).


вы хотели бы использовать __slots__ Если вы собираетесь создать много (сотни, тысячи) объектов того же класса. __slots__ существует только как инструмент оптимизации памяти.

Это очень не рекомендуется использовать __slots__ для ограничения создания атрибута, и в целом вы хотите избежать его, потому что он нарушает рассол, наряду с некоторыми другими функциями интроспекции python.


каждый объект python имеет __dict__ atttribute, который является словарем, содержащим все остальные атрибуты. например, при вводе self.attr python фактически делает self.__dict__['attr']. Как вы можете себе представить, использование словаря для хранения атрибута занимает дополнительное пространство и время для доступа к нему.

однако, когда вы используете __slots__ любой объект, созданный для этого класса не было!-Атрибут -0-->. Вместо этого весь доступ к атрибутам осуществляется непосредственно через указатели.

Итак, если хотите стиль C структура, а не полноценный класс, который вы можете использовать __slots__ для уплотнения размера объектов и сокращения времени доступа к атрибутам. Хорошим примером является класс Point, содержащий атрибуты x & y. Если у вас будет много очков, вы можете попробовать использовать __slots__ для того, чтобы сохранить память.


В дополнение к другим ответам, вот пример использования __slots__:

>>> class Test(object):   #Must be new-style class!
...  __slots__ = ['x', 'y']
... 
>>> pt = Test()
>>> dir(pt)
['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__', 
 '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', 
 '__repr__', '__setattr__', '__slots__', '__str__', 'x', 'y']
>>> pt.x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: x
>>> pt.x = 1
>>> pt.x
1
>>> pt.z = 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute 'z'
>>> pt.__dict__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute '__dict__'
>>> pt.__slots__
['x', 'y']

Итак, для реализации __slots__, требуется только дополнительная строка (и делает ваш класс классом нового стиля, если это еще не так). Таким образом, вы можете уменьшите объем памяти этих классов в 5 раз, за счет необходимости писать пользовательский код рассола, если и когда это станет необходимым.


слоты очень полезны для вызовов библиотек, чтобы исключить "именованную отправку метода" при выполнении вызовов функций. Это упоминается в SWIG документация. Для высокопроизводительных библиотек, которые хотят уменьшить накладные расходы функций для обычно называемых функций с помощью слотов, намного быстрее.

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


атрибут экземпляра класса имеет 3 свойства: например, имя атрибута и значение атрибута.

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

экземпляр(атрибут) --> стоимостью

на __слот__ доступ к, имя атрибута действует как словарь и экземпляр действуют как ключ в словаре, ищущий значение.

(экземпляр) --> стоимостью

на flyweight pattern, имя атрибута действует как словарь, а значение действует как ключ в этом словаре, просматривая экземпляр.

(значение) --> экземпляр


У вас-по существу - нет пользы для __slots__.

за то время, когда вы думаете, что вам может понадобиться __slots__, вы действительно хотите использовать легкий или Flyweight шаблоны проектирования. Это случаи, когда вы больше не хотите использовать чисто объекты Python. Вместо этого вам нужна объектно-подобная оболочка Python вокруг массива, структуры или массива numpy.

class Flyweight(object):

    def get(self, theData, index):
        return theData[index]

    def set(self, theData, index, value):
        theData[index]= value

оболочка, подобная классу, не имеет атрибутов - она просто предоставляет методы, которые действуют на базовые данные. Методы можно свести к методам класса. Действительно, он может быть сведен к функциям, работающим с базовым массивом данных.


очень простой пример .

Проблема: Без __slots__

если у меня нет __slot__ атрибут в моем классе я могу добавить новые атрибуты к моим объектам.

class Test:
    pass

obj1=Test()
obj2=Test()

print(obj1.__dict__)  #--> {}
obj1.x=12
print(obj1.__dict__)  # --> {'x': 12}
obj1.y=20
print(obj1.__dict__)  # --> {'x': 12, 'y': 20}

obj2.x=99
print(obj2.__dict__)  # --> {'x': 99}

если вы посмотрите на пример выше, вы можете увидеть, что obj1 и obj2 у своей x и y атрибуты и python также создали . Теперь я не могу добавлять новые атрибуты к моим объектам (кроме attribute x) и python не создать


еще одно несколько неясное использование __slots__ - добавление атрибутов в прокси-сервер объекта из пакета ProxyTypes, ранее входившего в проект PEAK. Его ObjectWrapper позволяет прокси - другому объекту, но перехватывать все взаимодействия с прокси-объектом. Он не очень часто используется (и нет поддержки Python 3), но мы использовали его для реализации потокобезопасной блокирующей оболочки вокруг асинхронной реализации на основе tornado, которая отскакивает от всего доступа к проксированному объекту через ioloop, использование thread-safe concurrent.Future объекты для синхронизации и возвращать результаты.

по умолчанию любой атрибут доступа к прокси-объекту даст вам результат от проксируемого объекта. Если вам нужно добавить атрибут на прокси-объект, __slots__ можно использовать.

from peak.util.proxies import ObjectWrapper

class Original(object):
    def __init__(self):
        self.name = 'The Original'

class ProxyOriginal(ObjectWrapper):

    __slots__ = ['proxy_name']

    def __init__(self, subject, proxy_name):
        # proxy_info attributed added directly to the
        # Original instance, not the ProxyOriginal instance
        self.proxy_info = 'You are proxied by {}'.format(proxy_name)

        # proxy_name added to ProxyOriginal instance, since it is
        # defined in __slots__
        self.proxy_name = proxy_name

        super(ProxyOriginal, self).__init__(subject)

if __name__ == "__main__":
    original = Original()
    proxy = ProxyOriginal(original, 'Proxy Overlord')

    # Both statements print "The Original"
    print "original.name: ", original.name
    print "proxy.name: ", proxy.name

    # Both statements below print 
    # "You are proxied by Proxy Overlord", since the ProxyOriginal
    # __init__ sets it to the original object 
    print "original.proxy_info: ", original.proxy_info
    print "proxy.proxy_info: ", proxy.proxy_info

    # prints "Proxy Overlord"
    print "proxy.proxy_name: ", proxy.proxy_name
    # Raises AttributeError since proxy_name is only set on 
    # the proxy object
    print "original.proxy_name: ", proxy.proxy_name

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

вот сравнение создания деревьев объектов с миллионом записей, используя слоты и без слотов. В качестве ссылки также производительность при использовании простых диктов для деревьев (Py2.7.10 on OSX):

********** RUN 1 **********
1.96036410332 <class 'css_tree_select.element.Element'>
3.02922606468 <class 'css_tree_select.element.ElementNoSlots'>
2.90828204155 dict
********** RUN 2 **********
1.77050495148 <class 'css_tree_select.element.Element'>
3.10655999184 <class 'css_tree_select.element.ElementNoSlots'>
2.84120798111 dict
********** RUN 3 **********
1.84069895744 <class 'css_tree_select.element.Element'>
3.21540498734 <class 'css_tree_select.element.ElementNoSlots'>
2.59615707397 dict
********** RUN 4 **********
1.75041103363 <class 'css_tree_select.element.Element'>
3.17366290092 <class 'css_tree_select.element.ElementNoSlots'>
2.70941114426 dict

тестовые классы (идент дикие слоты):

class Element(object):
    __slots__ = ['_typ', 'id', 'parent', 'childs']
    def __init__(self, typ, id, parent=None):
        self._typ = typ
        self.id = id
        self.childs = []
        if parent:
            self.parent = parent
            parent.childs.append(self)

class ElementNoSlots(object): (same, w/o slots)

testcode, режим verbose:

na, nb, nc = 100, 100, 100
for i in (1, 2, 3, 4):
    print '*' * 10, 'RUN', i, '*' * 10
    # tree with slot and no slot:
    for cls in Element, ElementNoSlots:
        t1 = time.time()
        root = cls('root', 'root')
        for i in xrange(na):
            ela = cls(typ='a', id=i, parent=root)
            for j in xrange(nb):
                elb = cls(typ='b', id=(i, j), parent=ela)
                for k in xrange(nc):
                    elc = cls(typ='c', id=(i, j, k), parent=elb)
        to =  time.time() - t1
        print to, cls
        del root

    # ref: tree with dicts only:
    t1 = time.time()
    droot = {'childs': []}
    for i in xrange(na):
        ela =  {'typ': 'a', id: i, 'childs': []}
        droot['childs'].append(ela)
        for j in xrange(nb):
            elb =  {'typ': 'b', id: (i, j), 'childs': []}
            ela['childs'].append(elb)
            for k in xrange(nc):
                elc =  {'typ': 'c', id: (i, j, k), 'childs': []}
                elb['childs'].append(elc)
    td = time.time() - t1
    print td, 'dict'
    del droot