Short answer
Unfortunately, non-empty __slots__ flat-out don't work with any kind of tuple, not just namedtuple().
Work around
The best you can do is to have a class that doesn't inherit from tuple.
I wrote one for you (see below). It is used like this:
from __future__ import print_function
from altnamedtuple import AltNamedTuple
from weakref import proxy
class Point(AltNamedTuple):
__slots__ = ('x', 'y', '__weakref__')
def __init__(self, x, y):
self.x = x
self.y = y
# Now, exercise the capabilities of the named tuple alternative
p = Point(10, 20)
r = proxy(p)
print(len(r)) # sizeable
print(r[0]) # indexed access
print(r.y) # attribute access
print(list(r)) # iterable
x, y = r # unpackable
print(x, y)
print(20 in r) # membership testing
print(tuple(reversed(p))) # reversible
print(r == (10, 20)) # equality
print(r != (30, 40)) # inequality
print(hash(p)) # hashable
print(r) # nice repr
print(r._asdict()) # conversion to a dict
print(r._replace(y=2))
t = (11, 22)
print(r.count(10))
print(r.index(20))
print(Point._make(t)) # alternative constructor
Note, weakrefs proxies don't pass through calls to __hash__ or __reversed__. That's an intrinsic limitation that no class can work around.
Source code for AltNamedTuple
Here's the class that does all the work:
class AltNamedTuple(object):
"Subclasser needs to define: __slots__ and __init__"
__slots__ = ()
def __getattr__(self, attr):
if attr != '_fields': raise AttributeError(attr)
if '__weakref__' in self.__slots__:
return self.__slots__[:-1]
return self.__slots__
def __len__(self):
return len(self._fields)
def __getitem__(self, index):
attr = self._fields[index]
return getattr(self, attr)
def __iter__(self):
for attr in self._fields:
yield getattr(self, attr)
def __reversed__(self):
return iter(reversed(tuple(self)))
def __eq__(self, other):
return tuple(self) == tuple(other)
def __ne__(self, other):
return tuple(self) != tuple(other)
def __hash__(self):
return hash(tuple(self))
def __repr__(self):
pairs = ['%s=%r' % (a, getattr(self, a)) for a in self._fields]
return ('%s(%s)' % (self.__class__.__name__, ', '.join(pairs)))
@classmethod
def _make(cls, iterable):
return cls(*iterable)
def _asdict(self):
return dict(zip(self._fields, self))
def _replace(_self, **kwds):
d = _self._asdict()
d.update(kwds)
return _self.__class__(**d)
def count(self, value):
return tuple(self).count(value)
def index(self, value):
return tuple(self).index(value)