只读对象模型的SqlAlchemy优化

Car*_*rlS 9 python performance sqlalchemy readonly

我有一个复杂的对象网络,使用sqlalchemy ORM映射从sqlite数据库中生成.我有很多深层嵌套:

for parent in owner.collection: 
    for child in parent.collection: 
        for foo in child.collection: 
            do lots of calcs with foo.property 
Run Code Online (Sandbox Code Playgroud)

我的分析表明,sqlalchemy仪器在这个用例中花了很多时间.

问题是:我不会在运行时更改对象模型(映射属性),因此一旦加载它我就不需要仪器,或者根本不需要任何sqlalchemy开销.经过大量的研究,我想我可能必须从我已经加载的"仪表化对象"中克隆一组"纯python"对象,但那将是一种痛苦.

性能在这里非常重要(它是一个模拟器),所以也许最好使用sqlite api将这些层写为C扩展.有什么想法吗?

Ant*_*sma 9

如果您多次引用单个实例的单个属性,则一个简单的技巧是将其存储在局部变量中.

如果您想要一种创建廉价纯python克隆的方法,请与原始对象共享dict对象:

class CheapClone(object):
    def __init__(self, original):
        self.__dict__ = original.__dict__
Run Code Online (Sandbox Code Playgroud)

创建这样的副本会花费大约一半的检测属性访问和属性查找,这与正常情况一样快.

可能还有一种方法可以让映射器创建未经检测的类的实例而不是已检测的类.如果我有时间,我可以看看根深蒂固的假设是填充的实例与检测类的类型相同.


找到一种快速而肮脏的方式,似乎至少在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
Run Code Online (Sandbox Code Playgroud)

用法示例:

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
Run Code Online (Sandbox Code Playgroud)