summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/util/_collections.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-08-28 12:25:21 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2014-08-28 12:25:21 -0400
commit685a014c644477a7e7cdb6aad4436d4422167209 (patch)
tree269adc73f4d1615167e8c6f8737561cfe32df6d7 /lib/sqlalchemy/util/_collections.py
parent00862a29c6c1494f1b55c3f93e5300f69fb4ac98 (diff)
downloadsqlalchemy-685a014c644477a7e7cdb6aad4436d4422167209.tar.gz
- A new implementation for :class:`.KeyedTuple` used by the
:class:`.Query` object offers dramatic speed improvements when fetching large numbers of column-oriented rows. fixes #3176
Diffstat (limited to 'lib/sqlalchemy/util/_collections.py')
-rw-r--r--lib/sqlalchemy/util/_collections.py70
1 files changed, 55 insertions, 15 deletions
diff --git a/lib/sqlalchemy/util/_collections.py b/lib/sqlalchemy/util/_collections.py
index 0904d454e..a1fbc0fa0 100644
--- a/lib/sqlalchemy/util/_collections.py
+++ b/lib/sqlalchemy/util/_collections.py
@@ -17,7 +17,20 @@ import types
EMPTY_SET = frozenset()
-class KeyedTuple(tuple):
+class AbstractKeyedTuple(tuple):
+ def keys(self):
+ """Return a list of string key names for this :class:`.KeyedTuple`.
+
+ .. seealso::
+
+ :attr:`.KeyedTuple._fields`
+
+ """
+
+ return list(self._fields)
+
+
+class KeyedTuple(AbstractKeyedTuple):
"""``tuple`` subclass that adds labeled names.
E.g.::
@@ -56,23 +69,13 @@ class KeyedTuple(tuple):
def __new__(cls, vals, labels=None):
t = tuple.__new__(cls, vals)
- t._labels = []
if labels:
t.__dict__.update(zip(labels, vals))
- t._labels = labels
+ else:
+ labels = []
+ t.__dict__['_labels'] = labels
return t
- def keys(self):
- """Return a list of string key names for this :class:`.KeyedTuple`.
-
- .. seealso::
-
- :attr:`.KeyedTuple._fields`
-
- """
-
- return [l for l in self._labels if l is not None]
-
@property
def _fields(self):
"""Return a tuple of string key names for this :class:`.KeyedTuple`.
@@ -86,7 +89,10 @@ class KeyedTuple(tuple):
:meth:`.KeyedTuple.keys`
"""
- return tuple(self.keys())
+ return tuple([l for l in self._labels if l is not None])
+
+ def __setattr__(self, key, value):
+ raise AttributeError("Can't set attribute: %s" % key)
def _asdict(self):
"""Return the contents of this :class:`.KeyedTuple` as a dictionary.
@@ -100,6 +106,40 @@ class KeyedTuple(tuple):
return dict((key, self.__dict__[key]) for key in self.keys())
+class _LW(AbstractKeyedTuple):
+ __slots__ = ()
+
+ def __new__(cls, vals):
+ return tuple.__new__(cls, vals)
+
+ def __reduce__(self):
+ # for pickling, degrade down to the regular
+ # KeyedTuple, thus avoiding anonymous class pickling
+ # difficulties
+ return KeyedTuple, (list(self), self._real_fields)
+
+ def _asdict(self):
+ """Return the contents of this :class:`.KeyedTuple` as a dictionary."""
+
+ d = dict(zip(self._real_fields, self))
+ d.pop(None, None)
+ return d
+
+
+def lightweight_named_tuple(name, fields):
+
+ tp_cls = type(name, (_LW,), {})
+ for idx, field in enumerate(fields):
+ if field is None:
+ continue
+ setattr(tp_cls, field, property(operator.itemgetter(idx)))
+
+ tp_cls._real_fields = fields
+ tp_cls._fields = tuple([f for f in fields if f is not None])
+
+ return tp_cls
+
+
class ImmutableContainer(object):
def _immutable(self, *arg, **kw):
raise TypeError("%s object is immutable" % self.__class__.__name__)