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

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

Пример использования

# -*- coding: utf-8 -*-
import cPickle as pickle
from Dictionary import Dictionary

# Создаем пустой объект "animalsDict"
animalsDict = Dictionary()

# Добавляем ветку "cats" и ноду "wild" со значением "Tiger"
animalsDict.cats.wild = 'Tiger'
# К ветке "cats" добавляем ноду "home" со значение "House"
animalsDict.cats.home = 'House'
# Добавляем ветку "dogs" одной строкой с нодами "wild" и "home"
animalsDict.dogs = Dictionary(wild='wolf', home='House')
# Выводим данные с префиксом "animals"
print "before dump:"
animalsDict.printf('animals')

# Сохраняем объект "animalsDict" в строку
data = pickle.dumps(animalsDict)
# Восстанавливаем данные в объект "animalsRestored"
animalsRestored = pickle.loads(data)

# Выводим данные с префиксом "animals2"
print
print "after dump:"
animalsRestored.printf("animals2")

# Удаляем ветку cats
del animalsRestored.cats

# Повторно выводим данные
print
print "after remove:"
animalsRestored.printf("animals2")

# Создаем объект только для чтения
p = Dictionary(read_only=True, x=2, y=3.5, z=1.0)

# Выводим объект "p" с префиксом "point"
print
print "read only:"
p.printf("point")

# Если это раскомментировать, будет исключение типа KeyError
# del p.x

Вывод

before dump:
animals.cats.home = House
animals.cats.wild = Tiger
animals.dogs.home = House
animals.dogs.wild = wolf

after dump:
animals2.cats.home = House
animals2.cats.wild = Tiger
animals2.dogs.home = House
animals2.dogs.wild = wolf

after remove:
animals2.dogs.home = House
animals2.dogs.wild = wolf

read only:
point.x = 2
point.y = 3.5
point.z = 1.0

Модуль Dictionary.py

Исходники программы на Python

import cPickle as pickle
 
class Dictionary:
    def __init__(self, read_only=False, **params):
        self.__dict__['__children__'] = {}
        self.__dict__['__readonly__'] = read_only
        for key, value in params.iteritems():
            self.__dict__['__children__'][key] = value
 
 
    def clear(self):
        for key, value in self.__dict__['__children__'].iteritems():
            if isinstance(value, Dictionary):
                value.clear()
        self.__dict__['__children__'].clear()
 
 
    def printf(self, prefix=None):
        for key in sorted(self.__dict__['__children__'].iterkeys()):
            value = self.__dict__['__children__'][key]
            if value is None:
                continue
            if isinstance(value, Dictionary):
                if prefix:
                    value.printf('%s.%s' % (prefix, key))
                else:
                    value.printf('%s' % key)
            else:
                if prefix:
                    print '%s.%s = %s' % (prefix, key, str(value))
                else:
                    print '%s = %s' % (key, str(value))
 
 
    def __getattr__(self, name):
        try:
            return self.__dict__['__children__'][name]
        except KeyError:
            if not self.__dict__['__readonly__']:
                value = self.__dict__['__children__'][name] = Dictionary()
                return value
            raise
 
 
    def __delattr__(self, name):
        if self.__dict__['__readonly__']:
            raise KeyError, "Read only structure."
        del self.__dict__['__children__'][name]
 
 
    def __setattr__(self, name, value):
        children = self.__dict__['__children__']
        old_value = children.get(name)
 
        if name not in children:
            if self.__dict__['__readonly__']:
                raise KeyError, "Read only structure."
        elif isinstance(old_value, Dictionary):
            if old_value:
                raise AttributeError, "Attribute %s has children: %s" % (repr(name), old_value)
 
        children[name] = value
 
 
    def __getitem__(self, name):
        return self.__getattr__(name)
 
 
    def __setitem__(self, name, value):
        self.__setattr__(name, value)
 
 
    def __delitem__(self, name):
        self.__delattr__(name)
 
 
    def __eq__(self, value):
        if isinstance(value, Dictionary):
            return repr(self) == repr(value)
        if isinstance(value, dict):
            return self.__dict__['__children__'] == value
        return value is None
 
 
    def __ne__(self, value):
        return not self.__eq__(value)
 
 
    def __nonzero__(self):
        return bool(self.__dict__['__children__'])
 
 
    def __str__(self):
        return str(repr(self))
 
 
    def __repr__(self):
        params = [('read_only=%s' % self.__dict__['__readonly__'])]
        for key in sorted(self.__dict__['__children__'].iterkeys()):
            value = self.__dict__['__children__'][key]
            params.append('%s=%s' % (key, repr(value)))
        return 'Dictionary(%s)' % ', '.join(params)
 
 
    def __len__(self):
        return len(self.__dict__['__children__'])
 
 
    def iterkeys(self):
        return self.__dict__['__children__'].iterkeys()
 
 
    def itervalues(self):
        return self.__dict__['__children__'].itervalues()
 
 
    def iteritems(self):
        return self.__dict__['__children__'].iteritems()
 
 
    def __iter__(self):
        return self.__dict__['__children__'].__iter__()
 
 
    def __setstate__(self, state):
        for key, name, value in state:
            if key == 'param':
                self.__dict__[name] = value
            elif key == 'value':
                self.__dict__['__children__'][name] = pickle.loads(value)
 
 
    def __getstate__(self):
        result = []
        result.append(('param', '__readonly__', self.__dict__['__readonly__']))
 
        for name, value in self.__dict__['__children__'].iteritems():
            result.append(('value', name, pickle.dumps(value)))
        return tuple(result)
 
 
    def __getinitargs__(self):
        return ()
 
 
    @staticmethod
    def parse(kwargs):
        kwargs = dict(kwargs)
        params = {}
 
        while kwargs:
            key, value = kwargs.popitem()
            uri = key.split(".")
            params_ptr = params
 
            if len(uri) == 1:
                params[key] = value
                continue
 
            for i in xrange(len(uri)):
                k = uri[i]
                if i == len(uri) - 1:
                    params_ptr[k] = value
                elif k not in params_ptr:
                    params_ptr[k] = {}
                params_ptr = params_ptr[k]
 
        def create(dictionary):
            result = {}
            for key, value in dictionary.iteritems():
                if isinstance(value, dict):
                    result[key] = create(value)
                else:
                    result[key] = value
            return Dictionary(read_only=True, **result)
 
        return create(params)

Использование precompiled headers в CMake – 2

Еще один один вариант использования precompiled headers в CMake. Подсмотрено в Ogre3D:

#-------------------------------------------------------------------
# This file is part of the CMake build system for OGRE
#     (Object-oriented Graphics Rendering Engine)
# For the latest info, see http://www.ogre3d.org/
#
# The contents of this file are placed in the public domain. Feel
# free to make use of it in any way you like.
#-------------------------------------------------------------------

##################################################################
# Support macro to use a precompiled header
# Usage:
#   use_precompiled_header(TARGET HEADER_FILE SRC_FILE)
##################################################################

macro(use_precompiled_header TARGET HEADER_FILE SRC_FILE)
  get_filename_component(HEADER ${HEADER_FILE} NAME)

  if (MSVC)
    add_definitions(/Yu"${HEADER}")
    set_source_files_properties(${SRC_FILE}
      PROPERTIES COMPILE_FLAGS /Yc"${HEADER}"
    )
    
  elseif (CMAKE_COMPILER_IS_GNUCXX)
    # disabled because it seems to increase compile time
    ## this is some serious hack... we definitely need native 
    ## support in CMake for this!
    ## we will generate the precompiled header via a workaround
    ## first give the header a new name with the proper extension
    #set(PRECOMP_HEADER ${CMAKE_CURRENT_BINARY_DIR}/hacked/${HEADER}.gch)
    #configure_file(${HEADER_FILE} ${PRECOMP_HEADER} COPYONLY)
    ## retrieve some info about the target's build settings
    #get_target_property(${TARGET} PRECOMP_TYPE TYPE)
    #if (PRECOMP_TYPE STREQUAL "SHARED_LIBRARY")
    #  set(PRECOMP_LIBTYPE "SHARED")
    #else ()
    #  set(PRECOMP_LIBTYPE "STATIC")
    #endif ()
    #get_target_property(${TARGET} PRECOMP_DEFINITIONS COMPILE_DEFINITIONS)
    #get_target_property(${TARGET} PRECOMP_FLAGS COMPILE_FLAGS)
    #
    ## add a new target which compiles the header
    #add_library(__precomp_header ${PRECOMP_LIBTYPE} ${PRECOMP_HEADER})
    #add_dependencies(${TARGET} __precomp_header)
    #set_target_properties(__precomp_header PROPERTIES
    #  COMPILE_DEFINITIONS ${PRECOMP_DEFINITIONS}
    #  COMPILE_FLAGS ${PRECOMP_FLAGS}
    #  HAS_CXX TRUE
    #)
    #set_source_files_properties(${PRECOMP_HEADER} PROPERTIES
    #  HEADER_FILE_ONLY FALSE
    #  KEEP_EXTENSION TRUE
    #  COMPILE_FLAGS "-x c++-header"
    #  LANGUAGE CXX
    #)
    #
    ## finally, we need to ensure that gcc can find the precompiled header
    ## this is another dirty hack
    #include_directories(BEFORE "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/__precomp_header.dir/hacked")

  endif ()
endmacro(use_precompiled_header)

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

FILE( GLOB_RECURSE SRC *.cpp )
USE_PRECOMPILED_HEADER( projectName "precompiled.h" "precompiled.cpp" )

UPDATE: Окончательный вариант precompiled headers смотрим здесь.

Личный блог Евгения Жирнова