Python/FAQ/Классы, Объекты и связи

Материал из Wiki.crossplatform.ru

Перейти к: навигация, поиск
· Python ·

Содержание

Introduction

#-----------------------------
# Inside a module named 'Data' / file named 'Data.py'
class Encoder(object):
    pass
 
#-----------------------------
obj = [3, 5]
print type(obj), id(obj), ob[1]
 
## Changing the class of builtin types is not supported
## in Python.
 
#-----------------------------
obj.Stomach = "Empty"    # directly accessing an object's contents
 
obj.NAME = "Thag"        # uppercase field name to make it stand out
(optional)
#-----------------------------
encoded = object.encode("data")
#-----------------------------
encoded = Data.Encoder.encode("data")
#-----------------------------
 
class Class(object):
    def __init__(self):
        pass
#-----------------------------
object = Class()
 
#-----------------------------
class Class(object):
    def class_only_method():
        pass # more code here
    class_only_method = staticmethod(class_only_method)
 
 
#-----------------------------
class Class(object):
    def instance_only_method(self):
        pass # more code here
#-----------------------------
 
lector = Human.Cannibal()
lector.feed("Zak")
lector.move("New York")
#-----------------------------
# NOTE: it is rare to use these forms except inside of
# methods to call specific methods from a parent class
lector = Human.Cannibal()
Human.Cannibal.feed(lector, "Zak")
Human.Cannibal.move(lector, "New York")
#-----------------------------
 
print>>sys.stderr, "stuff here\n"

Constructing an Object

#-----------------------------
class Class(object):
    pass
#-----------------------------
import time
 
class Class(object):
    def __init__(self):
        self.start = time.time()  # init data fields
        self.age = 0
 
#-----------------------------
import time
class Class(object):
    def __init__(self, **kwargs):
        # Sets self.start to the current time, and self.age to 0.  If called
 
        # with arguments, interpret them as key+value pairs to
        # initialize the object with
        self.age = 0
        self.__dict__.update(kwargs)
#-----------------------------

Destroying an Object

#-----------------------------
import time
 
class Class(object):
    def __del__(self):
        print self, "dying at", time.ctime()
#-----------------------------
 
## Why is the perl code introducing a cycle?  I guess it's an
## example of how to keep from calling the finalizer
self.WHATEVER = self
#-----------------------------

Managing Instance Data

#-----------------------------
# It is standard practice to access attributes directly:
class MyClass(object)
    def __init__(self):
        self.name = "default"
 
        self.age = 0
obj = MyClass()
obj.name = "bob"
print obj.name
obj.age += 1
 
# If you later find that you need to compute an attribute, you can always 
# retrofit a property(), leaving user code untouched:
class MyClass(object):
    def __init__(self):
        self._name = "default"
 
        self._age = 0
 
    def get_name(self):
        return self._name
    def set_name(self, name):
        self._name = name.title()
    name = property(get_name, set_name)
 
    def get_age(self):
        return self._age
    def set_age(self, val):
        if val < 0:
            raise ValueError("Invalid age: %s" % val)
        self._age = val
    age = property(get_age, set_age)
obj = MyClass()
obj.name = "bob"
 
print obj.name
obj.age += 1
 
# DON'T DO THIS - explicit getters and setters should not be used:
class MyClass(object):
    def __init__(self):
        self.name = "default"
 
    def get_name(self):
        return self.name
    def set_name(self, name):
        self.name = name.title()
obj = MyClass()
obj.set_name("bob")
 
print obj.get_name()
#-----------------------------
## DON'T DO THIS (It's complex, ugly, and unnecessary):
class MyClass(object):
    def __init__(self):
        self.age = 0
    def name(self, *args):
        if len(args) == 0:
            return self.name
        elif len(args) == 1:
            self.name = args[0]
        else:
 
            raise TypeError("name only takes 0 or 1 arguments")
    def age(self, *args):
        prev = self.age
        if args:
            self.age = args[0]
        return prev
 
 
# sample call of get and set: happy birthday!
obj.age(1 + obj.age())
 
#-----------------------------
him = Person()
him.NAME = "Sylvester"
him.AGE = 23
#-----------------------------
# Here's another way to implement the 'obj.method()' is a getter
# and 'obj.method(value)' is a settor.  Again, this is not a
# common Python idiom and should not be used.  See below for a
 
# more common way to do parameter checking of attribute assignment.
 
import re, sys
 
def carp(s):
    sys.stderr.write("WARNING: " + s + "\n")
 
class Class:
    no_name = []
 
    def name(self, value = no_name):
        if value is Class.no_name:
            return self.NAME
        value = self._enforce_name_value(value)
        self.NAME = value
 
    def _enforce_name_value(self, value):
        if re.search(r"[^\s\w'-]", value):
            carp("funny characters in name")
        if re.search(r"\d", value):
            carp("numbers in name")
        if not re.search(r"\S+(\s+\S+)+", value):
            carp("prefer multiword name")
        if not re.search(r"\S", value):
            carp("name is blank")
        return value.upper()   # enforce capitalization
 
#-----------------------------
# A more typical way to enforce restrictions on a value
# to set
class Class:
    def __setattr__(self, name, value):
        if name == "name":
            value = self._enforce_name_value(value)  # Do any conversions
 
        self.__dict__[name] = value  # Do the default __setattr__ action
 
    def _enforce_name_value(self, value):
        if re.search(r"[^\s\w'-]", value):
            carp("funny characters in name")
        if re.search(r"\d", value):
            carp("numbers in name")
        if not re.search(r"\S+(\s+\S+)+", value):
            carp("prefer multiword name")
        if not re.search(r"\S", value):
            carp("name is blank")
        return value.upper()   # enforce capitalization
 
 
#-----------------------------
class Person:
    def __init__(self, name = None, age = None, peers = None):
        if peers is None: peers = []  # See Python FAQ 6.25
 
        self.name = name
        self.age = age
        self.peers = peers
 
    def exclaim(self):
        return "Hi, I'm %s, age %d, working with %s" % \
            (self.name, self.age, ", ".join(self.peers))
 
    def happy_birthday(self):
        self.age += 1
        return self.age
 
#-----------------------------

Managing Class Data

#-----------------------------
## In the module named 'Person' ...
def population():
    return Person.body_count[0]
 
class Person(object):
    body_count = [0]        # class variable - shared across all instances
 
 
    def __init__(self):
        self.body_count[0] += 1
 
    def __del__(self):      # Beware - may be non-deterministic (Jython)!
 
        self.body_count[0] -= 1
 
# later, the user can say this:
import Person
people = []
for i in range(10):
    people.append(Person.Person())
print "There are", Person.population(), "people alive."
 
#=> There are 10 people alive.
#-----------------------------
him = Person()
him.gender = "male"
 
her = Person()
her.gender = "female"
 
#-----------------------------
 
FixedArray.max_bounds = 100                # set for whole class
alpha = FixedArray.FixedArray()
print "Bound on alpha is", alpha.max_bounds
#=>100
 
beta = FixedArray.FixedArray()
beta.max_bounds = 50                      # still sets for whole class
print "Bound on alpha is", alpha.max_bounds
 
#=>50
#-----------------------------
# In the module named 'FixedArray'
 
class FixedArray(object):
    _max_bounds = [7]        # Shared across whole class
 
    def __init__(self, bounds=None):
        if bounds is not None:
            self.max_bounds = bounds
 
    def get_max_bounds(self):
        return self._max_bounds[0]
    def set_max_bounds(self, val):
        self._max_bounds[0] = val
    max_bounds = property(get_max_bounds, set_max_bounds)
 
#-----------------------------

Using Classes as Structs

#-----------------------------
# There isn't the severe separation between scalar, arrays and hashs
# in Python, so there isn't a direct equivalent to the Perl code.
class Person:
    def __init__(self, name=None, age=None, peers=None):
        if peers is None: 
            peers = []
        self.name = name
        self.age = age
        self.peers = peers
 
p = Person("Jason Smythe", 13, ["Wilbur", "Ralph", "Fred"])
 
 
# or this way.  (This is not the prefered style as objects should
# be constructed with all the appropriate data, if possible.)
 
p = Person()  # allocate an empty Person
p.name = "Jason Smythe"                         # set its name field
p.age = 13                                      # set its age field
p.peers.extend( ["Wilbur", "Ralph", "Fred" ] )  # set its peers field
 
 
p.peers = ["Wilbur", "Ralph", "Fred"]
 
p.peers[:]= ["Wilbur", "Ralph", "Fred"]
 
# fetch various values, including the zeroth friend
print "At age %d, %s's first friend is %s." % \
    (p.age, p.name, p.peers[0])
 
#-----------------------------
# This isn't very Pythonic - should create objects with the
# needed data, and not depend on defaults and modifing the object.
import sys
def carp(s):
    sys.stderr.write("WARNING: " + s + "\n")
 
 
class Person:
    def __init__(self, name = "", age = 0):
        self.name = name
        self.age = age
    def __setattr__(self, name, value):
        if name == "age":
            # This is very unpythonic
 
            if not isinstance(value, type(0)):
                carp("age '%s' isn't numeric" % (value,))
            if value > 150: carp("age '%s' is unreasonable" % (value,))
        self.__dict__[name] = value
 
 
class Family:
    def __init__(self, head = None, address = "", members = None):
        if members is None: members = []
        self.head = head or Person()
        self.address = address
        self.members = members
 
folks = Family()
 
dad = folks.head
dad.name = "John"
 
dad.age = 34
 
print "%s's age is %d" % (folks.head.name, folks.head.age)
#-----------------------------
class Card:
    def __init__(self, name=None, color=None, cost=None,
                 type=None, release=None, text=None):
        self.name = name
        self.color = color
        self.cost = cost
        self.type = type
 
        self.release = release
        self.type = type
#-----------------------------
# For positional args
class Card:
    _names = ("name", "color", "cost", "type", "release", "type")
    def __init__(self, *args):
        assert len(args) <= len(self._names)
        for k, v in zip(self._names, args):
            setattr(self, k, None)
 
#-----------------------------
# For keyword args
class Card:
    _names = ("name", "color", "cost", "type", "release", "type")
    def __init__(self, **kwargs):
        for k in self._names:  # Set the defaults
 
            setattr(self, k, None)
        for k, v in kwargs.items():  # add in the kwargs
            assert k in self._names, "Unexpected kwarg: " + k
            setattr(self, k, v)
 
#-----------------------------
class hostent:
    def __init__(self, addr_list = None, length = None,
                 addrtype = None, aliases = None, name = None):
        self.addr_list = addr_list or []
        self.length = length or 0
        self.addrtype = addrtype or ""
 
        self.aliases = aliases or []
        self.name = name or ""
#-----------------------------
## XXX What do I do with these?
#define h_type h_addrtype
 
#define h_addr h_addr_list[0]
#-----------------------------
# make (hostent object)->type() same as (hostent object)->addrtype()
#
# *hostent::type = \&hostent::addrtype;
#
# # make (hostenv object)->
 
# addr()
#  same as (hostenv object)->addr_list(0)
#sub hostent::addr { shift->addr_list(0,@_) }
#-----------------------------
# No equivalent to Net::hostent (Python uses an unnamed tuple)
#package Extra::hostent;
#use Net::hostent;
#@ISA = qw(hostent);
 
#sub addr { shift->addr_list(0,@_) }
#1;
#-----------------------------

Cloning Objects

#-----------------------------
class Class(Parent):
    pass
#-----------------------------
 
## Note: this is unusual in Python code
ob1 = SomeClass()
# later on
ob2 = ob1.__class__()
#-----------------------------
## Note: this is unusual in Python code
ob1 = Widget()
ob2 = ob1.__class__()
#-----------------------------
# XXX I do not know the intent of the original Perl code
# Do not use this style of programming in Python.
 
import time
class Person(possible,base,classes):
    def __init__(self, *args, **kwargs):
        # Call the parents' constructors, if there are any
        for baseclass in self.__class__.__bases__:
            init = getattr(baseclass, "__init__")
            if init is not None:
                init(self, *args, **kwargs)
        self.PARENT = parent      # init data fields
 
        self.START = time.time()
        self.AGE = 0
#-----------------------------

Calling Methods Indirectly

#-----------------------------
methname = "flicker"
getattr(obj, methname)(10)    # calls obj->flicker(10);
 
 
# call three methods on the object, by name
for m in ("start", "run", "stop"):
    getattr(obj, m)()
 
#-----------------------------
methods = ("name", "rank", "serno")
his_info = {}
for m in methods:
   his_info[m] = getattr(ob, m)()
 
 
# same as this:
 
his_info = {
    'name': ob.name(),
    'rank': ob.rank(),
    'serno': ob.serno(),
}
#-----------------------------
fnref = ob.method
#-----------------------------
fnref(10, "fred")
 
#-----------------------------
obj.method(10, "fred")
#-----------------------------
# XXX Not sure if this is the correct translation.
# XXX Is 'can' special?
if isinstance(obj_target, obj.__class__):
    obj.can('method_name')(obj_target, *arguments)
 
#-----------------------------

Determining Subclass Membership

#-----------------------------
isinstance(obj, mimetools.Message)
issubclass(obj.__class__, mimetools.Message)
 
if hasattr(obj, "method_name"):  # check method validity
 
    pass
#-----------------------------
## Explicit type checking is needed fewer times than you think.
his_print_method = getattr(obj, "as_string", None)
#-----------------------------
__version__ = (3, 0)
Some_Module.__version__
 
 
# Almost never used, and doesn't work for builtin types, which don't
# have a __module__.
 
his_vers = obj.__module__.__version__
#-----------------------------
if Some_Module.__version__ < (3, 0):
  raise ImportError("Some_Module version %s is too old, expected (3, 0)" %
                    (Some_Module.__version__,))
 
# or more simply
assert Some_Module.__version__ >= (3, 0), "version too old"
 
#-----------------------------
__VERSION__ = '1.01'
#-----------------------------

Writing an Inheritable Class

#-----------------------------
 
# Note: This uses the standard Python idiom of accessing the
# attributes directly rather than going through a method call.
# See earlier in this chapter for examples of how this does
# not break encapsulation.
class Person:
    def __init__(self, name = "", age = 0):
        self.name = name
        self.age = age
 
#-----------------------------
# Prefered: dude = Person("Jason", 23)
dude = Person()
dude.name = "Jason"
dude.age = 23
print "%s is age %d." % (dude.name, dude.age)
#-----------------------------
class Employee(Person):
    pass
 
#-----------------------------
# Prefered: empl = Employee("Jason", 23)
emp = Employee()
empl.name = "Jason"
empl.age = 23
print "%s is age %d." % (empl.name, empl.age)
#-----------------------------

Accessing Overridden Methods

#-----------------------------
 
# This doesn't need to be done since if 'method' doesn't
# exist in the Class it will be looked for in its BaseClass(es)
class Class(BaseClass):
    def method(self, *args, **kwargs):
        BaseClass.method(self, *args, **kwargs)
 
# This lets you pick the specific method in one of the base classes
 
class Class(BaseClass1, BaseClass2):
    def method(self, *args, **kwargs):
        BaseClass2.method(self, *args, **kwargs)
 
# This looks for the first method in the base class(es) without
# specifically knowing which base class.  This reimplements
# the default action so isn't really needed.
 
class Class(BaseClass1, BaseClass2, BaseClass3):
    def method(self, *args, **kwargs):
        for baseclass in self.__class__.__bases__:
            f = getattr(baseclass, "method")
            if f is not None:
                return f(*args, **kwargs)
        raise NotImplementedError("method")
 
 
#-----------------------------
self.meth()   # Call wherever first meth is found
 
Where.meth(self)  # Call in the base class "Where"
 
# XXX Does Perl only have single inheritence?  Or does
# it check all base classes?  No directly equivalent way
 
# to do this in Python, but see above.
#-----------------------------
import time
 
# The Perl code calls a private '_init' function, but in
# Python there's need for the complexity of 'new' mechanism
# so it's best just to put the '_init' code in '__init__'.
class Class:
    def __init__(self, *args):
        # init data fields
 
        self.START = time.time()
        self.AGE = 0
        self.EXTRA = args          # anything extra
#-----------------------------
obj = Widget(haircolor = "red", freckles = 121)
#-----------------------------
class Class(Base1, Base2, Base3):
    def __init__(self, *args, **kwargs):
        for base in self.__class__.__bases__:
            f = getattr(base, "__init__")
            if f is not None:
                f(self, *args, **kwargs)
 
#-----------------------------

Generating Attribute Methods Using AUTOLOAD

#-----------------------------
# NOTE: Python prefers direct attribute lookup rather than
# method calls.  Python 2.2 will introduce a 'get_set' which
# *may* be equivalent, but I don't know enough about it.  So
# instead I'll describe a class that lets you restrict access
# to only specific attributes.
 
class Private:
    def __init__(self, names):
        self.__names = names
        self.__data = {}
    def __getattr__(self, name):
        if name in self.__names:
            return self.__data[name]
        raise AttributeError(name)
    def __setattr__(self, name, value):
        if name.startswith("_Private"):
            self.__dict__[name] = value
            return
 
        if name in self.__names:
            self.__data[name] = value
            return
        raise TypeError("cannot set the attribute %r" % (name,))
 
 
class Person(Private):
    def __init__(self, parent = None):
        Private.__init__(self, ["name", "age", "peers", "parent"])
        self.parent = parent
    def new_child(self):
        return Person(self)
 
#-----------------------------
dad = Person()
dad.name = "Jason"
dad.age = 23
kid = dad.new_child()
kid.name = "Rachel"
kid.age = 2
print "Kid's parent is", kid.parent.name
#=>Kid's parent is Jason

Solving the Data Inheritance Problem

#-----------------------------
 
## XXX No clue on what this does.  For that matter, what's
## "The Data Inheritance Problem"?

Coping with Circular Data Structures

#-----------------------------
node.NEXT = node
#-----------------------------
# This is not a faithful copy of the Perl code, but it does
# show how to have the container's __del__ remove cycles in
# its contents.  Note that Python 2.0 includes a garbage
# collector that is able to remove these sorts of cycles, but
 
# it's still best to prevent cycles in your code.
class Node:
    def __init__(self, value = None):
        self.next = self
 
        self.prev = self
        self.value = value
 
class Ring:
    def __init__(self):
        self.ring = None
 
        self.count = 0
 
    def __str__(self):
        # Helpful when debugging, to print the contents of the ring
        s = "#%d: " % self.count
        x = self.ring
        if x is None:
            return s
        values = []
        while True:
            values.append(x.value)
            x = x.next
            if x is self.ring:
                break
 
        return s + " -> ".join(map(str, values)) + " ->"
 
    def search(self, value):
        node = self.ring
        while True:
            if node.value == value:
                return node
            node = node.next
            if node is self.ring:
                break
 
    def insert_value(self, value):
        node = Node(value)
        if self.ring is not None:
            node.prev, node.next = self.ring.prev, self.ring
            self.ring.prev.next = self.ring.prev = node
        self.ring = node
        self.count += 1
 
    def delete_value(self, value):
        node = self.search(value)
        if node is not None:
            self.delete_node(node)
 
    def delete_node(self, node):
        if node is node.next:
            node.next = node.prev = None
 
            self.ring = None
        else:
            node.prev.next, node.next.prev = node.next, node.prev
            if node is self.ring:
                self.ring = node.next
        self.count -= 1
 
    def __del__(self):
        while self.ring is not None:
            self.delete_node(self.ring)
 
COUNT = 1000
 
for rep in range(20):
    r = Ring()
    for i in range(COUNT):
        r.insert_value(i)
#-----------------------------

Overloading Operators

#-----------------------------
 
import UserString
class MyString(UserString.UserString):
    def __cmp__(self, other):
        return cmp(self.data.upper(), other.upper())
 
 
class Person:
    def __init__(self, name, idnum):
        self.name = name
        self.idnum = idnum
    def __str__(self):
        return "%s (%05d)" % (self.name.lower().capitalize(), self.idnum)
 
 
#-----------------------------
class TimeNumber:
    def __init__(self, hours, minutes, seconds):
        assert minutes < 60 and seconds < 60
        self.hours = hours
        self.minutes = minutes
        self.seconds = seconds
    def __str__(self):
        return "%d:%02d:%02d" % (self.hours, self.minutes, self.seconds)
    def __add__(self, other):
        seconds = self.seconds + other.seconds
        minutes = self.minutes + other.minutes
        hours = self.hours + other.hours
        if seconds >= 60:
            seconds %= 60
            minutes += 1
        if minutes >= 60:
            minutes %= 60
            hours += 1
        return TimeNumber(hours, minutes, seconds)
 
    def __sub__(self, other):
        raise NotImplementedError
 
    def __mul__(self, other):
        raise NotImplementedError
 
    def __div__(self, other):
        raise NotImplementedError
 
t1 = TimeNumber(0, 58, 59)
sec = TimeNumber(0, 0, 1)
min = TimeNumber(0, 1, 0)
print t1 + sec + min + min
# 1:01:00
 
#-----------------------------
 
# For demo purposes only - the StrNum class is superfluous in this
# case as plain strings would give the same result.
class StrNum:
    def __init__(self, value):
        self.value = value
 
    def __cmp__(self, other):  # both <=> and cmp
 
        # providing <=> gives us <, ==, etc. for free.
        # __lt__, __eq__, and __gt__ can also be individually specified
        return cmp(self.value, other.value)
 
    def __str__(self):  # ""
 
        return self.value
 
    def __nonzero__(self, other):   # bool
        return bool(self.value)
 
    def __int__(self, other):   # 0+
 
        return int(self.value)
 
    def __add__(self, other):   # +
        return StrNum(self.value + other.value)
 
    def __radd__(self, other):   # +, inverted
 
        return StrNum(other.value + self.value)
 
    def __mul__(self, other):   # *
        return StrNum(self.value * other)
 
    def __rmul__(self, other):   # *, inverted
 
        return StrNum(self.value * other)
 
 
def demo():
    # show_strnum - demo operator overloading
    x = StrNum("Red")
    y = StrNum("Black")
    z = x + y
    r = z * 3
    print "values are %s, %s, %s, and %s" % (x, y, z, r)
    if x < y:
        s = "LT"
 
    else:
        s = "GE"
    print x, "is", s, y
 
if __name__ == "__main__":
    demo()
 
# values are Red, Black, RedBlack, and RedBlackRedBlackRedBlack
# Red is GE Black
 
#-----------------------------
#!/usr/bin/env python
# demo_fixnum - show operator overloading
 
# sum of STRFixNum: 40 and STRFixNum: 12 is STRFixNum: 52
# product of STRFixNum: 40 and STRFixNum: 12 is STRFixNum: 480
# STRFixNum: 3 has 0 places
 
# div of STRFixNum: 40 by STRFixNum: 12 is STRFixNum: 3.33
# square of that is  STRFixNum: 11.11
 
# This isn't excatly the same as the original Perl code since
# I couldn't figure out why the PLACES variable was used.
#-----------------------------
import re
_places_re = re.compile(r"\.(\d+)")
 
default_places = 0
 
class FixNum:
    def __init__(self, value, places = None):
        self.value = value
        if places is None:
            # get from the value
 
            m = _places_re.search(str(value))
            if m:
                places = int(m.group(1))
            else:
                places = default_places
        self.places = places
 
    def __add__(self, other):
        return FixNum(self.value + other.value,
                      max(self.places, other.places))
 
    def __mul__(self, other):
        return FixNum(self.value * other.value,
                      max(self.places, other.places))
 
    def __div__(self, other):
        # Force to use floating point, since 2/3 in Python is 0
 
        # Don't use float() since that will convert strings
        return FixNum((self.value+0.0) / other.value,
                      max(self.places, other.places))
 
    def __str__(self):
        return "STR%s: %.*f" % (self.__class__.__name__,
                                self.places, self.value)
    def __int__(self):
        return int(self.value)
 
    def __float__(self):
        return self.value
 
 
def demo():
    x = FixNum(40)
    y = FixNum(12, 0)
 
    print "sum of", x, "and", y, "is", x+y
    print "product of", x, "and", y, "is", x*y
 
    z = x/y
    print "%s has %d places" % (z, z.places)
    if not z.places:
        z.places = 2
 
    print "div of", x, "by", y, "is", z
    print "square of that is ", z*z
 
 
if __name__ == "__main__":
    demo()

Creating Magic Variables with tie

# You can't tie a variable, but you can use properties.  
import itertools
class ValueRing(object):
    def __init__(self, colours):
        self.colourcycle = itertools.cycle(colours)
 
    def next_colour(self):
        return self.colourcycle.next()
    colour = property(next_colour)
vr = ValueRing(["red", "blue"])
 
for i in range(6):
    print vr.colour,
print
 
# Note that you MUST refer directly to the property
x = vr.colour
print x, x, x
 
#-------------------------------------
# Ties are generally unnecessary in Python because of its strong OO support -
# The resulting code is MUCH shorter:
class AppendDict(dict):
    def __setitem__(self, key, val):
        if key in self:
            self[key].append(val)
        else:
 
            super(AppendDict, self).__setitem__(key, [val])
tab = AppendDict()
tab["beer"] = "guinness"
tab["food"] = "potatoes"
tab["food"] = "peas"
 
for key, val in tab.items():
    print key, "=>", val
#-------------------------------------
class CaselessDict(dict):
    def __setitem__(self, key, val):
        super(CaselessDict, self).__setitem__(key.lower(), val)
    def __getitem__(self, key):
        return super(CaselessDict, self).__getitem__(key.lower())
 
tab = CaselessDict()
tab["VILLAIN"] = "big "
 
tab["herOine"] = "red riding hood"
tab["villain"] = "bad wolf"
 
for key, val in tab.items():
    print key, "is", val
 
#=>villain is bad wolf
#=>heroine is red riding hood
#-------------------------------------
class RevDict(dict):
    def __setitem__(self, key, val):
        super(RevDict, self).__setitem__(key, val)
        super(RevDict, self).__setitem__(val, key)
 
tab = RevDict()
tab["red"] = "rojo"
 
tab["blue"] = "azul"
tab["green"] = "verde"
tab["evil"] = ("No Way!", "Way!")
 
 
for key, val in tab.items():
    print key, "is", val
#=>blue is azul
#=>('No Way!', 'Way!') is evil
 
#=>rojo is red
#=>evil is ('No Way!', 'Way!')
#=>azul is blue
#=>verde is green
#=>green is verde
#=>red is rojo
 
#-------------------------------------
import itertools
for elem in itertools.count():
    print "Got", elem
#-------------------------------------
 
# You could use FileDispatcher from section 7.18
tee = FileDispatcher(sys.stderr, sys.stdout)
#-------------------------------------