У меня есть сложная сеть объектов, порождаемых от sqlite базы данных с помощью sqlalchemy ORM отображения. У меня есть довольно многие глубоко вложенные:
for parent in owner.collection:
for child in parent.collection:
for foo in child.collection:
do lots of calcs with foo.property
Мое профилирование показывает мне, что sqlalchemy инструментарий занимает много времени в этом варианте использования.
Вещь: Я никогда не изменяю объектную модель (отображенные свойства) во времени выполнения, поэтому как только они загружаются, мне не НУЖЕН инструментарий или действительно любой sqlalchemy наверху вообще. После большого исследования я думаю, что мне, возможно, придется клонировать 'чистый Python' набор объектов от моих уже загруженных 'оснащенных объектов', но это будет болью.
Производительность действительно крайне важна здесь (это - средство моделирования), поэтому возможно, пишущий те слои, поскольку C расширения с помощью sqlite API непосредственно было бы лучшим. Какие-либо мысли?
Если вы много раз ссылаетесь на один атрибут одного экземпляра, простой трюк состоит в том, чтобы сохранить его в локальной переменной.
Если вам нужен способ создания дешевых чистых клонов Python, поделитесь объектом dict с исходным объектом:
class CheapClone(object):
def __init__(self, original):
self.__dict__ = original.__dict__
Создание такой копии стоит около половины инструментального доступа к атрибутам, а поиск атрибутов выполняется так же быстро, как обычно.
Также может быть способ заставить преобразователь создавать экземпляры неинструментированного класса вместо инструментированного. Если у меня будет время, я мог бы посмотреть, насколько глубоко укоренилось предположение о том, что заполненные экземпляры относятся к тому же типу, что и инструментированный класс.
Нашел быстрый и грязный способ, который, кажется, хоть немного работает с 0.5.8 и 0.6. Не тестировал его с помощью наследования или других функций, которые могут плохо взаимодействовать. Кроме того, это касается некоторых непубличных API, поэтому остерегайтесь поломки при изменении версии.
from sqlalchemy.orm.attributes import ClassManager, instrumentation_registry
class ReadonlyClassManager(ClassManager):
"""Enables configuring a mapper to return instances of uninstrumented
classes instead. To use add a readonly_type attribute referencing the
desired class to use instead of the instrumented one."""
def __init__(self, class_):
ClassManager.__init__(self, class_)
self.readonly_version = getattr(class_, 'readonly_type', None)
if self.readonly_version:
# default instantiation logic doesn't know to install finders
# for our alternate class
instrumentation_registry._dict_finders[self.readonly_version] = self.dict_getter()
instrumentation_registry._state_finders[self.readonly_version] = self.state_getter()
def new_instance(self, state=None):
if self.readonly_version:
instance = self.readonly_version.__new__(self.readonly_version)
self.setup_instance(instance, state)
return instance
return ClassManager.new_instance(self, state)
Base = declarative_base()
Base.__sa_instrumentation_manager__ = ReadonlyClassManager
Пример использования:
class ReadonlyFoo(object):
pass
class Foo(Base, ReadonlyFoo):
__tablename__ = 'foo'
id = Column(Integer, primary_key=True)
name = Column(String(32))
readonly_type = ReadonlyFoo
assert type(session.query(Foo).first()) is ReadonlyFoo
Попробуйте использовать одиночный запрос с JOINами вместо циклов python.
У вас должна быть возможность отключить отложенную загрузку для рассматриваемых отношений, и sqlalchemy извлечет их все за один запрос.