]> granicus.if.org Git - python/commitdiff
Add list.sorted() classmethod.
authorRaymond Hettinger <python@rcn.com>
Wed, 29 Oct 2003 06:54:43 +0000 (06:54 +0000)
committerRaymond Hettinger <python@rcn.com>
Wed, 29 Oct 2003 06:54:43 +0000 (06:54 +0000)
Doc/lib/libstdtypes.tex
Lib/UserList.py
Lib/test/test_sort.py
Misc/NEWS
Objects/listobject.c

index bffcea94773e9dbe7b590b0b88f7b0bff61fc665..fec0ede2a7df1646f17751a1fedc6e2d80730bbf 100644 (file)
@@ -629,7 +629,7 @@ there is at least one character, false otherwise.
 
 \begin{methoddesc}[string]{istitle}{}
 Return true if the string is a titlecased string and there is at least one
-character, i.e. uppercase characters may only follow uncased
+character, for example uppercase characters may only follow uncased
 characters and lowercase characters only cased ones.  Return false
 otherwise.
 \end{methoddesc}
@@ -975,6 +975,9 @@ The following operations are defined on mutable sequence types (where
   \lineiii{\var{s}.sort(\optional{\var{cmp}=None\optional{, \var{key}=None
                         \optional{, \var{reverse}=False}}})}
        {sort the items of \var{s} in place}{(7), (8), (9), (10)}
+  \lineiii{\var{s}.sorted(\var{iterable}\optional{, \var{cmp}=None\optional{, \var{key}=None
+                        \optional{, \var{reverse}=False}}})}
+       {return a new sorted list from the items in \var{iterable}}{(8), (9), (11)}     
 \end{tableiii}
 \indexiv{operations on}{mutable}{sequence}{types}
 \indexiii{operations on}{sequence}{types}
@@ -1024,8 +1027,8 @@ Notes:
   list.  To remind you that they operate by side effect, they don't return
   the sorted or reversed list.
 
-\item[(8)] The \method{sort()} method takes optional arguments for
-  controlling the comparisions.
+\item[(8)] The \method{sort()} and \method{sorted()} methods take optional
+  arguments for controlling the comparisions.
 
   \var{cmp} specifies a custom comparison function of two arguments
      (list items) which should return a negative, zero or positive number
@@ -1052,7 +1055,8 @@ Notes:
   \versionchanged[Support for \var{key} and \var{reverse} was added]{2.4}
 
 \item[(9)]  Starting with Python 2.3, the \method{sort()} method is
-  guaranteed to be stable.  A sort is stable if it guarantees not to
+  guaranteed to be stable.  Starting with Python 2.4, the \method{sorted()}
+  method is also guaranteed to be stable.  A sort is stable if it does not
   change the relative order of elements that compare equal --- this is
   helpful for sorting in multiple passes (for example, sort by
   department, then by salary grade).
@@ -1062,6 +1066,9 @@ Notes:
   of Python 2.3 makes the list appear empty for the duration, and raises
   \exception{ValueError} if it can detect that the list has been
   mutated during a sort.
+
+\item[(11)] \method{sorted()} is a class method that returns a new list.
+  \versionadded{2.4}
 \end{description}
 
 
index 072f6a7327b4023038c7ee9a9dea537ee6c15a1d..e8fd356adb067d856d429404a7f9dc9ac7743bb5 100644 (file)
@@ -78,6 +78,11 @@ class UserList:
     def index(self, item, *args): return self.data.index(item, *args)
     def reverse(self): self.data.reverse()
     def sort(self, *args, **kwds): self.data.sort(*args, **kwds)
+    def sorted(cls, iterable, *args, **kwds):
+        s = cls(iterable)
+        s.sort(*args, **kwds)
+        return s
+    sorted = classmethod(sorted)
     def extend(self, other):
         if isinstance(other, UserList):
             self.data.extend(other.data)
index ca6fc80144a144e59cb9ca56633ad16d3ce2c3a6..1c102307ba7f104816677533790d22f9d38db74d 100644 (file)
@@ -1,5 +1,7 @@
 from test.test_support import verbose
 import random
+from UserList import UserList
+from sets import Set
 
 nerrors = 0
 
@@ -190,6 +192,7 @@ class TestDecorateSortUndecorate(unittest.TestCase):
         random.shuffle(data)
         data.sort(reverse=True)
         self.assertEqual(data, range(99,-1,-1))
+        self.assertRaises(TypeError, data.sort, "wrong type")
 
     def test_reverse_stability(self):
         data = [(random.randrange(100), i) for i in xrange(200)]
@@ -201,11 +204,66 @@ class TestDecorateSortUndecorate(unittest.TestCase):
         copy2.sort(key=lambda x: x[0], reverse=True)
         self.assertEqual(data, copy2)
 
+class TestSorted(unittest.TestCase):
+
+    def test_basic(self):
+        data = range(100)
+        copy = data[:]
+        random.shuffle(copy)
+        self.assertEqual(data, list.sorted(copy))
+        self.assertNotEqual(data, copy)
+
+        data.reverse()
+        random.shuffle(copy)
+        self.assertEqual(data, list.sorted(copy, cmp=lambda x, y: cmp(y,x)))
+        self.assertNotEqual(data, copy)
+        random.shuffle(copy)
+        self.assertEqual(data, list.sorted(copy, key=lambda x: -x))
+        self.assertNotEqual(data, copy)
+        random.shuffle(copy)
+        self.assertEqual(data, list.sorted(copy, reverse=1))
+        self.assertNotEqual(data, copy)
+
+    def test_inputtypes(self):
+        s = 'abracadabra'
+        for T in [unicode, list, tuple]:
+            self.assertEqual(list.sorted(s), list.sorted(T(s)))
+
+        s = ''.join(dict.fromkeys(s).keys())  # unique letters only
+        for T in [unicode, Set, list, tuple, dict.fromkeys]:
+            self.assertEqual(list.sorted(s), list.sorted(T(s)))
+
+    def test_baddecorator(self):
+        data = 'The quick Brown fox Jumped over The lazy Dog'.split()
+        self.assertRaises(TypeError, list.sorted, data, None, lambda x,y: 0)
+
+    def classmethods(self):
+        s = "hello world"
+        a = list.sorted(s)
+        b = UserList.sorted(s)
+        c = [].sorted(s)
+        d = UserList().sorted(s)
+        class Mylist(list):
+            def __new__(cls):
+                return UserList()
+        e = MyList.sorted(s)
+        f = MyList().sorted(s)
+        class Myuserlist(UserList):
+            def __new__(cls):
+                return []
+        g = MyList.sorted(s)
+        h = MyList().sorted(s)
+        self.assert_(a == b == c == d == e == f == g == h)
+        self.assert_(b.__class__ == d.__class__ == UserList)
+        self.assert_(e.__class__ == f.__class__ == MyList)
+        self.assert_(g.__class__ == h.__class__ == Myuserlist)
+
 #==============================================================================
 
 def test_main(verbose=None):
     test_classes = (
         TestDecorateSortUndecorate,
+        TestSorted,
         TestBugs,
     )
 
index a3c5fe52625607f889dfcc17b7f6c0d2ff92b0c9..4bbbb5708e70a1cceb22d615c018af2bf36828ce 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -23,8 +23,8 @@ Core and builtins
   starting with Py2.3 are guaranteed to be stable (the relative order of
   records with equal keys is unchanged).
 
-- Added a list.copysort() method that returns a copy of the sorted list
-  while leaving the original intact.
+- Added a list.sorted() classmethod that returns a new sorted list
+  from any iterable.
 
 - Added test whether wchar_t is signed or not. A signed wchar_t is not
   usable as internal unicode type base for Py_UNICODE since the
index b53b948500a8a0acaefee596e99908ae36f85389..23d7d9a620c4729d19d32ee22d42104824130066 100644 (file)
@@ -2000,6 +2000,38 @@ PyList_Sort(PyObject *v)
        return 0;
 }
 
+static PyObject *
+listsorted(PyObject *cls, PyObject *args, PyObject *kwds)
+{
+       PyObject *newlist, *v, *seq, *compare=NULL, *keyfunc=NULL, *newargs;
+       static char *kwlist[] = {"iterable", "cmp", "key", "reverse", 0};
+       long reverse;
+
+       if (args != NULL) {
+               if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOi:sorted",
+                       kwlist, &seq, &compare, &keyfunc, &reverse))
+                       return NULL;
+       }
+
+       newlist = PyObject_CallFunctionObjArgs(cls, seq, NULL);
+       if (newlist == NULL)
+               return NULL;
+       
+       newargs = PyTuple_GetSlice(args, 1, 4);
+       if (newargs == NULL) {
+               Py_DECREF(newlist);
+               return NULL;
+       }
+       v = listsort((PyListObject *)newlist, newargs, kwds);
+       Py_DECREF(newargs);
+       if (v == NULL) {
+               Py_DECREF(newlist);
+               return NULL;
+       }
+       Py_DECREF(v);
+       return newlist;
+}
+
 static PyObject *
 listreverse(PyListObject *self)
 {
@@ -2335,6 +2367,9 @@ PyDoc_STRVAR(reverse_doc,
 PyDoc_STRVAR(sort_doc,
 "L.sort(cmp=None, key=None, reverse=False) -- stable sort *IN PLACE*;\n\
 cmp(x, y) -> -1, 0, 1");
+PyDoc_STRVAR(sorted_doc,
+"list.sorted(iterable, cmp=None, key=None, reverse=False) --> new sorted list;\n\
+cmp(x, y) -> -1, 0, 1");
 
 static PyMethodDef list_methods[] = {
        {"append",      (PyCFunction)listappend,  METH_O, append_doc},
@@ -2346,6 +2381,8 @@ static PyMethodDef list_methods[] = {
        {"count",       (PyCFunction)listcount,   METH_O, count_doc},
        {"reverse",     (PyCFunction)listreverse, METH_NOARGS, reverse_doc},
        {"sort",        (PyCFunction)listsort,    METH_VARARGS | METH_KEYWORDS, sort_doc},
+       {"sorted",      (PyCFunction)listsorted,  
+               METH_VARARGS | METH_KEYWORDS | METH_CLASS, sorted_doc},
        {NULL,          NULL}           /* sentinel */
 };