Roy said:
Terry Reedy said:
As I suggested elsewhere, make iter() the type object for iterator.
Then iter.reversed is closely parallel to list.sorted.
I've got a question about list.sorted. I assume it's just a list that's
initialized to have its elements sorted when it's created? We're not
talking about some new magic container type which keeps its elements
sorted all the time?
In other words, I'm assuming that
l = list.sorted ((1, 2, 5, 3))
l.append (4)
print l
will print [1, 2, 3, 5, 4], right?
Perfectly right. If you DO want a listoid container that keeps its
elements sorted, that's not hard to make for yourself. There are
basically two obvious implementation strategies:
-- ensure the underlying list is re-sorted at each insertion,
append &c, and/or
-- let the underlying list grow as it will on insertion &c, but
make sure it's sorted at any method that accesses it (you can
use a flag -- 'have there being any append/insert/... since
the last sort?' -- to re-sort only when needed).
I'll choose the first for general use because:
-- accesses are much more frequent than modifications,
-- list.sort is VERY fast when a list is "nearly sorted"
-- the second approach would require sorting also on many
kind of changes (e.g. __setitem__ -- to ensure the items
being replaced ARE those the user expects...)
&c.
So, we clearly start with:
class alwayssortedlist(list):
def __init__(self, *args):
list.__init__(self, *args)
self.sort()
We need to ensure sorting at creation. We _might_ make the sort
method in this class a noop and call list.sort(self) instead,
but, naah -- list.sort is SO fast when called on an already
sorted list that it ain't even funny, so, choose simplicity.
Onwards to simple mods:
def append(self, *args):
list.append(self, *args)
self.sort()
def __setitem__(self, *args):
list.__setitem__(self, *args)
self.sort()
....starting to see a pattern, by any chance...?-) Yep, ALL
list mutators we _need_ to override are exactly like this!
(We MAY override e.g. index and remove to take advantage
of the sortedness, but, that's optional...
. extend and
insert too (now, reverse IS an interesting issue...!-), and
__iadd__ and __imul__ (methods creating new lists, such as
__add__, __mul__ and __rmul__, are slightly different). Oh,
__setslice__, too.
Well, doing it in longhand is fine, of course, but why not
have a bit more fun with (not showing the __add__ &c):
class solist(list): pass
def solist_method(methname):
list_method = getattr(list, methname)
def method(self, *args):
list_method(self, *args)
self.sort()
setattr(solist, methname, method)
for n in 'init setitem iadd imul'.split:
solist_method('__%s__' % n)
for n in 'append extend insert'.split:
solist_method('%s' % n)
Not much shorter than the boilerplate/longhand, and not
perfect (we should make a new function object to give it
the right func_name -- as is, all the methods' names
will be "method" in tracebacks &c...
, but sorta fun.
Testing & completion left as an exercise to the student...
Alex