from collections import defaultdict

d = {'k1':1}

d["k1"]

d["k2"] # this will get an error because the k2 key doesnt exist

d = defaultdict(object)

d['one'] # this doesn't exist, but in calling for it it will create a new element {'one' : object}

d['two'] # same, this will add {'two' : object}

for k, v in d.items():
    print "key:", k, "item:", v

e = defaultdict(lambda: 0) # lambda just returns 0 here

e['four']
e['twelve']

def error():
    return 'error'

f = defaultdict(error) #returned item must be callable or None

f['new']

f.items()

"""
Explanation: defaultdict
The whole point is that it will always return a value even if you query for a key that doesnt exist. That value you  set ahead of time is called a factory object. That key also gets turned into a new key/value pair with the factory object
End of explanation
"""


d_norm = {}
d_norm['a'] = 1
d_norm['b'] = 2
d_norm['c'] = 3
d_norm['d'] = 4
d_norm['e'] = 5

# order isn't preserved since a dict is just a mapping
for k,v in d_norm.items():
    print k,v


from collections import OrderedDict

d_ordered = OrderedDict()
d_ordered['a'] = 1
d_ordered['b'] = 2
d_ordered['c'] = 3
d_ordered['d'] = 4
d_ordered['e'] = 5

for k,v in d_ordered.items():
    print k, v

from collections import namedtuple

# this is kind of like creating a new class on the fly
# the first parameter of a namedtuple is the name of the class/tuple type
# the second parameter is a space-delimeted list of properties of the tuple
Dog = namedtuple('Dog','age breed name')
sam = Dog(age=2, breed='Lab', name='Sammy')

print sam.age
print sam.breed
print sam.name

Catzz = namedtuple('Cat', 'fur claws name')
mittens = Catzz(fur='fuzzy', claws='sharp', name='Mittens')
print type(mittens)
print type(Catzz)
print mittens[0]
print mittens.claws
print mittens.name
print mittens.count('fuzzy')
print mittens.index('sharp')

"""
Explanation: orderedDict
dictionary subclass that remembers the order items were added
End of explanation
"""


