# 11 Other Language Changes

Here are all of the changes that Python 2.4 makes to the core Python language.

- Decorators for functions and methods were added (PEP 318).

- Built-in `set` and `frozenset` types were added (PEP 218). Other new built-ins include the `reversed(seq)` function (PEP 322).

- Generator expressions were added (PEP 289).

- Certain numeric expressions no longer return values restricted to 32 or 64 bits (PEP 237).

- You can now put parentheses around the list of names in a `from module import names` statement (PEP 328).

- The `dict.update()` method now accepts the same argument forms as the `dict` constructor. This includes any mapping, any iterable of key/value pairs, and keyword arguments. (Contributed by Raymond Hettinger.)

- The string methods `ljust()`, `rjust()`, and `center()` now take an optional argument for specifying a fill character other than a space. (Contributed by Raymond Hettinger.)

- Strings also gained an `rsplit()` method that works like the `split()` method but splits from the end of the string. (Contributed by Sean Reifschneider.)

      >>> 'www.python.org'.split('.', 1)
      ['www', 'python.org']
      'www.python.org'.rsplit('.', 1)
      ['www.python', 'org']

- Three keyword parameters, cmp, key, and reverse, were added to the `sort()` method of lists. These parameters make some common usages of `sort()` simpler. All of these parameters are optional.

  For the cmp parameter, the value should be a comparison function that takes two parameters and returns -1, 0, or +1 depending on how the parameters compare. This function will then be used to sort the list. Previously this was the only parameter that could be provided to `sort()`.

  key should be a single-parameter function that takes a list element and returns a comparison key for the element. The list is then sorted using the comparison keys. The following example sorts a list case-insensitively:

      >>> L = ['A', 'b', 'c', 'D']
      >>> L.sort()                 # Case-sensitive sort
      >>> L
      ['A', 'D', 'b', 'c']
      >>> # Using 'key' parameter to sort list
      >>> L.sort(key=lambda x: x.lower())
      >>> L
      ['A', 'b', 'c', 'D']
      >>> # Old-fashioned way
      >>> L.sort(cmp=lambda x,y: cmp(x.lower(), y.lower()))
      >>> L
      ['A', 'b', 'c', 'D']

  The last example, which uses the cmp parameter, is the old way to perform a case-insensitive sort. It works but is slower than using a key parameter. Using key calls `lower()` method once for each element in the list while using cmp will call it twice for each comparison, so using key saves on invocations of the `lower()` method.

  For simple key functions and comparison functions, it is often possible to avoid a `lambda` expression by using an unbound method instead. For example, the above case-insensitive sort is best written as:

      >>> L.sort(key=str.lower)
      >>> L
      ['A', 'b', 'c', 'D']

  Finally, the reverse parameter takes a Boolean value. If the value is true, the list will be sorted into reverse order. Instead of `L.sort() ; L.reverse()`, you can now write `L.sort(reverse=True)`.

  The results of sorting are now guaranteed to be stable. This means that two entries with equal keys will be returned in the same order as they were input. For example, you can sort a list of people by name, and then sort the list by age, resulting in a list sorted by age where people with the same age are in name-sorted order.

  (All changes to `sort()` contributed by Raymond Hettinger.)

- There is a new built-in function `sorted(iterable)` that works like the in-place `list.sort()` method but can be used in expressions. The differences are:
  - the input may be any iterable;
  - a newly formed copy is sorted, leaving the original intact; and
  - the expression returns the new sorted copy

      >>> L = [9,7,8,3,2,4,1,6,5]
      >>> [10+i for i in sorted(L)]       # usable in a list comprehension
      [11, 12, 13, 14, 15, 16, 17, 18, 19]
      >>> L                               # original is left unchanged
      [9,7,8,3,2,4,1,6,5]
      >>> sorted('Monty Python')          # any iterable may be an input
      [' ', 'M', 'P', 'h', 'n', 'n', 'o', 'o', 't', 't', 'y', 'y']

      >>> # List the contents of a dict sorted by key values
      >>> colormap = dict(red=1, blue=2, green=3, black=4, yellow=5)
      >>> for k, v in sorted(colormap.iteritems()):
      ...     print k, v
      ...
      black 4
      blue 2
      green 3
      red 1
      yellow 5

  (Contributed by Raymond Hettinger.)

- Integer operations will no longer trigger an `OverflowWarning`. The `OverflowWarning` warning will disappear in Python 2.5.

- The interpreter gained a new switch, -m, that takes a name, searches for the corresponding module on `sys.path`, and runs the module as a script. For example, you can now run the Python profiler with `python -m profile`. (Contributed by Nick Coghlan.)

- The `eval(expr, globals, locals)` and `execfile(filename, globals, locals)` functions and the `exec` statement now accept any mapping type for the locals parameter. Previously this had to be a regular Python dictionary. (Contributed by Raymond Hettinger.)

- The `zip()` built-in function and `itertools.izip()` now return an empty list if called with no arguments. Previously they raised a `TypeError` exception. This makes them more suitable for use with variable length argument lists:

      >>> def transpose(array):
      ...    return zip(*array)
      ...
      >>> transpose([(1,2,3), (4,5,6)])
      [(1, 4), (2, 5), (3, 6)]
      >>> transpose([])
      []

  (Contributed by Raymond Hettinger.)

- Encountering a failure while importing a module no longer leaves a partially-initialized module object in `sys.modules`. The incomplete module object left behind would fool further imports of the same module into succeeding, leading to confusing errors. (Fixed by Tim Peters.)

- `None` is now a constant; code that binds a new value to the name "`None`" is now a syntax error. (Contributed by Raymond Hettinger.)

## 11.1 Optimizations

- The inner loops for list and tuple slicing were optimized and now run about one-third faster. The inner loops for dictionaries were also optimized , resulting in performance boosts for `keys()`, `values()`, `items()`, `iterkeys()`, `itervalues()`, and `iteritems()`. (Contributed by Raymond Hettinger.)

- The machinery for growing and shrinking lists was optimized for speed and for space efficiency. Appending and popping from lists now runs faster due to more efficient code paths and less frequent use of the underlying system `realloc()`. List comprehensions also benefit. `list.extend()` was also optimized and no longer converts its argument into a temporary list before extending the base list. (Contributed by Raymond Hettinger.)

- `list()`, `tuple()`, `map()`, `filter()`, and `zip()` now run several times faster with non-sequence arguments that supply a `__len__()` method. (Contributed by Raymond Hettinger.)

- The methods `list.__getitem__()`, `dict.__getitem__()`, and `dict.__contains__()` are are now implemented as `method_descriptor` objects rather than `wrapper_descriptor` objects. This form of access doubles their performance and makes them more suitable for use as arguments to functionals: "`map(mydict.__getitem__, keylist)`". (Contributed by Raymond Hettinger.)

- Added a new opcode, `LIST_APPEND`, that simplifies the generated bytecode for list comprehensions and speeds them up by about a third. (Contributed by Raymond Hettinger.)

- The peephole bytecode optimizer has been improved to produce shorter, faster bytecode; remarkably, the resulting bytecode is more readable. (Enhanced by Raymond Hettinger.)

- String concatenations in statements of the form `s = s + "abc"` and `s += "abc"` are now performed more efficiently in certain circumstances. This optimization won't be present in other Python implementations such as Jython, so you shouldn't rely on it; using the `join()` method of strings is still recommended when you want to efficiently glue a large number of strings together. (Contributed by Armin Rigo.)

The net result of the 2.4 optimizations is that Python 2.4 runs the pystone benchmark around 5% faster than Python 2.3 and 35% faster than Python 2.2. (pystone is not a particularly good benchmark, but it's the most commonly used measurement of Python's performance. Your own applications may show greater or smaller benefits from Python 2.4.)