models: Switch to slots to reduce memory usage per instance
This commit is contained in:
parent
08fd99ffdb
commit
0fee1b4b11
@ -45,7 +45,7 @@ class Field(object):
|
||||
def __get__(self, instance, owner):
|
||||
if not instance:
|
||||
return self
|
||||
return instance.__dict__.get(self._name, self._default)
|
||||
return getattr(instance, '_' + self._name, self._default)
|
||||
|
||||
def __set__(self, instance, value):
|
||||
if value is not None:
|
||||
@ -54,10 +54,11 @@ class Field(object):
|
||||
if value is None or value == self._default:
|
||||
self.__delete__(instance)
|
||||
else:
|
||||
instance.__dict__[self._name] = value
|
||||
setattr(instance, '_' + self._name, value)
|
||||
|
||||
def __delete__(self, instance):
|
||||
instance.__dict__.pop(self._name, None)
|
||||
if hasattr(instance, '_' + self._name):
|
||||
delattr(instance, '_' + self._name)
|
||||
|
||||
|
||||
class String(Field):
|
||||
@ -134,12 +135,14 @@ class FieldOwner(type):
|
||||
"""Helper to automatically assign field names to descriptors."""
|
||||
|
||||
def __new__(cls, name, bases, attrs):
|
||||
attrs['_fields'] = []
|
||||
fields = {}
|
||||
for key, value in attrs.items():
|
||||
if isinstance(value, Field):
|
||||
attrs['_fields'].append(key)
|
||||
fields[key] = '_' + key
|
||||
value._name = key
|
||||
attrs['_fields'].sort()
|
||||
|
||||
attrs['_fields'] = fields
|
||||
attrs['__slots__'] = ['_' + field for field in fields]
|
||||
return super(FieldOwner, cls).__new__(cls, name, bases, attrs)
|
||||
|
||||
|
||||
@ -165,14 +168,23 @@ class ImmutableObject(object):
|
||||
super(ImmutableObject, self).__setattr__(key, value)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if name in self.__slots__:
|
||||
return super(ImmutableObject, self).__setattr__(name, value)
|
||||
raise AttributeError('Object is immutable.')
|
||||
|
||||
def __delattr__(self, name):
|
||||
if name in self.__slots__:
|
||||
return super(ImmutableObject, self).__delattr__(name)
|
||||
raise AttributeError('Object is immutable.')
|
||||
|
||||
def _items(self):
|
||||
for field, key in self._fields.items():
|
||||
if hasattr(self, key):
|
||||
yield field, getattr(self, key)
|
||||
|
||||
def __repr__(self):
|
||||
kwarg_pairs = []
|
||||
for (key, value) in sorted(self.__dict__.items()):
|
||||
for key, value in sorted(self._items()):
|
||||
if isinstance(value, (frozenset, tuple)):
|
||||
if not value:
|
||||
continue
|
||||
@ -185,15 +197,14 @@ class ImmutableObject(object):
|
||||
|
||||
def __hash__(self):
|
||||
hash_sum = 0
|
||||
for key, value in self.__dict__.items():
|
||||
for key, value in self._items():
|
||||
hash_sum += hash(key) + hash(value)
|
||||
return hash_sum
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, self.__class__):
|
||||
return False
|
||||
|
||||
return self.__dict__ == other.__dict__
|
||||
return dict(self._items()) == dict(other._items())
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
@ -224,7 +235,7 @@ class ImmutableObject(object):
|
||||
def serialize(self):
|
||||
data = {}
|
||||
data['__model__'] = self.__class__.__name__
|
||||
for key, value in self.__dict__.items():
|
||||
for key, value in self._items():
|
||||
if isinstance(value, (set, frozenset, list, tuple)):
|
||||
value = [
|
||||
v.serialize() if isinstance(v, ImmutableObject) else v
|
||||
|
||||
@ -29,15 +29,14 @@ class FieldDescriptorTest(unittest.TestCase):
|
||||
instance = create_instance(Field())
|
||||
self.assertIsNone(instance.attr)
|
||||
|
||||
def test_field_does_not_store_default_in_dict(self):
|
||||
def test_field_does_not_store_default(self):
|
||||
instance = create_instance(Field())
|
||||
self.assertNotIn('attr', instance.__dict__)
|
||||
self.assertFalse(hasattr(instance, '_attr'))
|
||||
|
||||
def test_field_assigment_and_retrival(self):
|
||||
instance = create_instance(Field())
|
||||
instance.attr = 1234
|
||||
self.assertEqual(1234, instance.attr)
|
||||
self.assertEqual(1234, instance.__dict__['attr'])
|
||||
|
||||
def test_field_can_be_reassigned(self):
|
||||
instance = create_instance(Field())
|
||||
@ -50,14 +49,14 @@ class FieldDescriptorTest(unittest.TestCase):
|
||||
instance.attr = 1234
|
||||
del instance.attr
|
||||
self.assertEqual(None, instance.attr)
|
||||
self.assertNotIn('attr', instance.__dict__)
|
||||
self.assertFalse(hasattr(instance, '_attr'))
|
||||
|
||||
def test_field_can_be_set_to_none(self):
|
||||
instance = create_instance(Field())
|
||||
instance.attr = 1234
|
||||
instance.attr = None
|
||||
self.assertEqual(None, instance.attr)
|
||||
self.assertNotIn('attr', instance.__dict__)
|
||||
self.assertFalse(hasattr(instance, '_attr'))
|
||||
|
||||
def test_field_can_be_set_default(self):
|
||||
default = object()
|
||||
@ -65,7 +64,7 @@ class FieldDescriptorTest(unittest.TestCase):
|
||||
instance.attr = 1234
|
||||
instance.attr = default
|
||||
self.assertEqual(default, instance.attr)
|
||||
self.assertNotIn('attr', instance.__dict__)
|
||||
self.assertFalse(hasattr(instance, '_attr'))
|
||||
|
||||
|
||||
class FieldTest(unittest.TestCase):
|
||||
@ -183,13 +182,11 @@ class CollectionTest(unittest.TestCase):
|
||||
instance = create_instance(Collection(type=int, container=frozenset))
|
||||
instance.attr = []
|
||||
self.assertEqual(frozenset(), instance.attr)
|
||||
self.assertNotIn('attr', instance.__dict__)
|
||||
|
||||
def test_collection_gets_stored_in_container(self):
|
||||
instance = create_instance(Collection(type=int, container=frozenset))
|
||||
instance.attr = [1, 2, 3]
|
||||
self.assertEqual(frozenset([1, 2, 3]), instance.attr)
|
||||
self.assertEqual(frozenset([1, 2, 3]), instance.__dict__['attr'])
|
||||
|
||||
def test_collection_with_wrong_type(self):
|
||||
instance = create_instance(Collection(type=int, container=frozenset))
|
||||
|
||||
@ -55,7 +55,7 @@ class GenericCopyTest(unittest.TestCase):
|
||||
|
||||
def test_copying_track_to_remove(self):
|
||||
track = Track(name='foo').copy(name=None)
|
||||
self.assertEqual(track.__dict__, Track().__dict__)
|
||||
self.assertFalse(hasattr(track, '_name'))
|
||||
|
||||
|
||||
class RefTest(unittest.TestCase):
|
||||
|
||||
Loading…
Reference in New Issue
Block a user