Si hace referencia a un solo atributo de una sola instancia muchas veces, un simple truco es almacenarlo en una variable local.
Si quieres una manera de crear clones pitón puros baratos, comparte el objeto dict con el objeto original:
class CheapClone(object):
def __init__(self, original):
self.__dict__ = original.__dict__
Creación de una copia como esto cuesta aproximadamente la mitad del acceso atributo instrumentado y atribuyen las búsquedas son tan rápido como es normal
También puede haber una manera de que el asignador cree instancias de una clase sin instrumentación en lugar de la instrumentada. Si tengo algo de tiempo, podría echar un vistazo a cuán arraigada está la suposición de que las instancias pobladas son del mismo tipo que la clase instrumentada.
Encontré una manera rápida y sucia que al menos parece funcionar en 0.5.8 y 0.6. No lo probé con herencia u otras características que podrían interactuar mal. Además, esto toca algunas API no públicas, así que ten cuidado con las roturas cuando cambies de versión.
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
Ejemplo de uso:
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
Desafortunadamente, el patrón de uso es muchos cálculos en muchos objetos pequeños, por lo que el almacenamiento en caché local no es tan útil. La idea de clonación realmente suena como el camino a seguir, gracias por el consejo rápido. Su comentario final es exactamente lo que me gustaría: pedirle al mapeador que cree una clase 'sin instrumentos', porque sé que es de solo lectura. – CarlS
¡Muchas gracias! No puedo esperar para probar esto. – CarlS
He hecho un trabajo inicial sobre el mapeo de mapeo sugerido y las diferencias de tiempo son alentadoras. Para un bucle simple: para i en xrange (500000): foo = readonlyobj.attr_bar con instrumentos normales: 2.663 segundos con truco asignador de sólo lectura: 0.078 segs Eso es un resultado muy significativo de la OMI, así que gracias de nuevo. Todavía estoy tratando de entender realmente cómo funciona y está demostrando ser una excelente manera de aprender sqlalchemy con más profundidad. – CarlS