diff --git a/advanced-classes.html b/advanced-classes.html index a931634..782bf58 100644 --- a/advanced-classes.html +++ b/advanced-classes.html @@ -27,49 +27,92 @@ body{counter-reset:h1 11}
[FIXME here's why ordered dicts are useful: http://www.gossamer-threads.com/lists/python/dev/656556 ]
import collections
-import itertools
-
-class OrderedDict(dict, collections.MutableMapping):
+class OrderedDict(dict, MutableMapping):
+ 'Dictionary that remembers insertion order'
+ # An inherited dict maps keys to values.
+ # The inherited dict provides __getitem__, __len__, __contains__, and get.
+ # The remaining methods are order-aware.
+ # Big-O running times for all methods are the same as for regular dictionaries.
+
+ # The internal self.__map dictionary maps keys to links in a doubly linked list.
+ # The circular doubly linked list starts and ends with a sentinel element.
+ # The sentinel element never gets deleted (this simplifies the algorithm).
+ # The prev/next links are weakref proxies (to prevent circular references).
+ # Individual links are kept alive by the hard reference in self.__map.
+ # Those hard references disappear when a key is deleted from an OrderedDict.
def __init__(self, *args, **kwds):
+ '''Initialize an ordered dictionary. Signature is the same as for
+ regular dictionaries, but keyword arguments are not recommended
+ because their insertion order is arbitrary.
+
+ '''
if len(args) > 1:
- raise TypeError('expected at most 1 arguments')
- if not hasattr(self, '_keys'):
- self._keys = []
+ raise TypeError('expected at most 1 arguments, got %d' % len(args))
+ try:
+ self.__root
+ except AttributeError:
+ self.__root = root = _Link() # sentinel node for the doubly linked list
+ root.prev = root.next = root
+ self.__map = {}
self.update(*args, **kwds)
def clear(self):
- del self._keys[:]
+ 'od.clear() -> None. Remove all items from od.'
+ root = self.__root
+ root.prev = root.next = root
+ self.__map.clear()
dict.clear(self)
def __setitem__(self, key, value):
+ 'od.__setitem__(i, y) <==> od[i]=y'
+ # Setting a new item creates a new link which goes at the end of the linked
+ # list, and the inherited dictionary is updated with the new key/value pair.
if key not in self:
- self._keys.append(key)
+ self.__map[key] = link = _Link()
+ root = self.__root
+ last = root.prev
+ link.prev, link.next, link.key = last, root, key
+ last.next = root.prev = _proxy(link)
dict.__setitem__(self, key, value)
def __delitem__(self, key):
+ 'od.__delitem__(y) <==> del od[y]'
+ # Deleting an existing item uses self.__map to find the link which is
+ # then removed by updating the links in the predecessor and successor nodes.
dict.__delitem__(self, key)
- self._keys.remove(key)
+ link = self.__map.pop(key)
+ link.prev.next = link.next
+ link.next.prev = link.prev
def __iter__(self):
- return iter(self._keys)
+ 'od.__iter__() <==> iter(od)'
+ # Traverse the linked list in order.
+ root = self.__root
+ curr = root.next
+ while curr is not root:
+ yield curr.key
+ curr = curr.next
def __reversed__(self):
- return reversed(self._keys)
-
- def popitem(self):
- if not self:
- raise KeyError('dictionary is empty')
- key = self._keys.pop()
- value = dict.pop(self, key)
- return key, value
+ 'od.__reversed__() <==> reversed(od)'
+ # Traverse the linked list in reverse order.
+ root = self.__root
+ curr = root.prev
+ while curr is not root:
+ yield curr.key
+ curr = curr.prev
def __reduce__(self):
+ 'Return state information for pickling'
items = [[k, self[k]] for k in self]
+ tmp = self.__map, self.__root
+ del self.__map, self.__root
inst_dict = vars(self).copy()
- inst_dict.pop('_keys', None)
- return (self.__class__, (items,), inst_dict)
+ self.__map, self.__root = tmp
+ if inst_dict:
+ return (self.__class__, (items,), inst_dict)
+ return self.__class__, (items,)
setdefault = MutableMapping.setdefault
update = MutableMapping.update
@@ -78,25 +121,54 @@ class OrderedDict(dict, collections.MutableMapping):
values = MutableMapping.values
items = MutableMapping.items
- def __repr__(self):
+ def popitem(self, last=True):
+ '''od.popitem() -> (k, v), return and remove a (key, value) pair.
+ Pairs are returned in LIFO order if last is true or FIFO order if false.
+
+ '''
if not self:
- return '{0}()'.format(self.__class__.__name__,)
- return '{0}({1})'.format(self.__class__.__name__, repr(list(self.items())))
+ raise KeyError('dictionary is empty')
+ key = next(reversed(self) if last else iter(self))
+ value = self.pop(key)
+ return key, value
+
+ def __repr__(self):
+ 'od.__repr__() <==> repr(od)'
+ if not self:
+ return '%s()' % (self.__class__.__name__,)
+ return '%s(%r)' % (self.__class__.__name__, list(self.items()))
def copy(self):
+ 'od.copy() -> a shallow copy of od'
return self.__class__(self)
@classmethod
def fromkeys(cls, iterable, value=None):
+ '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S
+ and values equal to v (which defaults to None).
+
+ '''
d = cls()
for key in iterable:
d[key] = value
return d
def __eq__(self, other):
+ '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive
+ while comparison to a regular mapping is order-insensitive.
+
+ '''
if isinstance(other, OrderedDict):
- return all(p==q for p, q in itertools.zip_longest(self.items(), other.items()))
- return dict.__eq__(self, other)
+ return len(self)==len(other) and \
+ all(p==q for p, q in zip(self.items(), other.items()))
+ return dict.__eq__(self, other)
+
+ def __ne__(self, other):
+ '''od.__ne__(y) <==> od!=y. Comparison to another OD is order-sensitive
+ while comparison to a regular mapping is order-insensitive.
+
+ '''
+ return not self == other
⁂ diff --git a/layout.css b/layout.css new file mode 100644 index 0000000..1c88c70 --- /dev/null +++ b/layout.css @@ -0,0 +1,74 @@ +/* + +"Dive Into Python 3" layout stylesheet + +Copyright (c) 2009, Mark Pilgrim, All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +@page { + size: US-Letter; + margin: 1.75in; + padding: 0; + @bottom-center { + font: 12pt/1.75 serif; + content: counter(page); + } +} +body, .w a { + font: 10pt/1.3 serif; +} +pre, kbd, samp, code, var, .b { + font: 8pt/1.3 monospace; +} +span { + font-size: 10pt; +} +.baa { + font-size: 11pt; +} +.q span { + font-size: 13pt; +} +.f:first-letter { + color: #888; + font: normal 48pt/0.68 serif; +} +p, ul, ol { + margin: 0; + font-size: 11pt; +} +p + p { + text-indent: 1em; +} + +h1 { + page-break-before: always; + prince-bookmark-level: 1; +} +h2 { + prince-bookmark-level: 2; +} +h3 { + prince-bookmark-level: 3; +} diff --git a/porting-code-to-python-3-with-2to3.html b/porting-code-to-python-3-with-2to3.html index cba5f0f..d48fc42 100644 --- a/porting-code-to-python-3-with-2to3.html +++ b/porting-code-to-python-3-with-2to3.html @@ -288,7 +288,7 @@ import whichdb
import xmlrpclib
+import xmlrpclib
import xmlrpc.client
import DocXMLRPCServer
diff --git a/util/flatten.py b/util/flatten.py
index 413d8a7..3f3bc8f 100644
--- a/util/flatten.py
+++ b/util/flatten.py
@@ -44,7 +44,7 @@ for filename in chapters:
for line in open(filename, encoding="utf-8"):
if line.count(''):
include = True
- if line.count('
©'):
include = False
if line.count('