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