Jeśli odwołujesz się do pojedynczego atrybutu pojedynczej instancji wiele razy, prostą sztuczką jest zapisanie jej w zmiennej lokalnej.
Jeśli chcesz sposobem na stworzenie tanich czystych klonów Pythonie udostępnić obiekt dict z oryginalnego obiektu:
class CheapClone(object):
def __init__(self, original):
self.__dict__ = original.__dict__
Tworzenie kopii takiego kosztuje około połowę oprzyrządowanego dostępu atrybut i atrybut wyszukiwań są szybko jak zwykle.
Może istnieć sposób, aby mapper tworzył instancje niezabezpieczonej klasy zamiast instrumentowanej. Jeśli mam trochę czasu, mógłbym rzucić okiem na to, jak głęboko zakorzenione jest założenie, że zaludnione instancje są tego samego typu co oprzyrządowana klasa.
Znaleziono szybki i brudny sposób, który wydaje się co najmniej nieco pracy na 0.5.8 i 0.6. Nie przetestowano go z dziedziczeniem lub innymi funkcjami, które mogą źle wchodzić w interakcje. Dotyczy to także niektórych niepublicznych interfejsów API, więc uważaj na awarie podczas zmiany wersji.
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
Przykład użycia:
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
Niestety, wzorzec użycia ma wiele obliczeń dla wielu małych obiektów, więc lokalne buforowanie nie jest tak pomocne. Pomysł klonowania naprawdę brzmi jak droga, dzięki za szybką wskazówkę. Twój końcowy komentarz jest dokładnie taki, jaki chciałbym: poproś programistę o stworzenie "niezabezpieczonej" klasy, ponieważ wiem, że jest to tylko do odczytu. – CarlS
Wielkie dzięki! Nie mogę się doczekać, aby to wypróbować. – CarlS
Zrobiłem kilka wstępnych prac dotyczących sugerowanego hakowania map, a różnice w czasie są zachęcające. Dla prostej pętli: bom w xrange (500000): foo = readonlyobj.attr_bar z normalnego oprzyrządowania: 2.663 sek z readonly odwzorowujący Hack: 0,078 sek To bardzo znaczący wynik imo, więc dzięki jeszcze raz. Nadal staram się naprawdę zrozumieć, jak to działa i jest to świetny sposób, aby nauczyć się sqlalchemy z nieco większą głębią. – CarlS