"""
Using python objects - strings, lists, and dictionaries.
"""


# All python variables are "objects"
# An object combines data + functions (called methods) that can be applied
# to the object's data.

# Example: A string - its data are the characters that make up the string.

s = 'python is fun'

# An object's methods can be accessed using the dot notation:

s.capitalize()

# Think of a method as a message that tells an object to do something,
# like return a version of itself capitalized.

# Other string methods:

# count how many times a given string occurs within our string
s.count('is')
# find the location where a given string occurs in our string:
s.find('is')

# our familiar split function can be accessed as a method:
s.split()

# and join as well:
l = ['a', 'b', 'c', 'd']
','.join(l)

# the alternative way of calling these methods is as functions in
# the string module:

import string
string.capitalize(s)
string.count(s, 'is')
string.find(s, 'is')

# A complete list of the methods of Python strings can be found at
# the Python documentation:
# http://docs.python.org/library/stdtypes.html#string-methods

# Here list methods:

s = ['a', 'b']
x = 'c'
# add an element x to a list s:
s.append(x)
# can also be done as:
s = s + [x]
# and also
s[len(s):len(s)] = [x]
# now if you want to concatenate two lists:
s = ['a', 'b']
x = ['c', 'd']
s.extend(x)
# or as we learned it without the use of methods:
s = s + x
# or using slices:
s[len(s):len(s)] = x


# create a list of 2**i for all i that are less than n
def powers_of_2(n) :
    values = []
    for i in range(n) :
        values.append(2**i)
    return values

# more methods:

s.count(x)
# the number of i's for which s[i] == x  

s.index(x[, i[, j]])
# the smallest k such that s[k] == x and i <= k < j

# insert an item in a list:
s.insert(i, x) 
# same as s[i:i] = [x]

# remove an item from a list
s.remove(x)
# same as del s[s.index(x)]

# reverse a list
s.reverse()
# sort a list
s.sort()

# see also:
# http://docs.python.org/library/stdtypes.html#mutable-sequence-types

# methods of Python dictionaries

d = {'key1':'value1', 'key2': 'value2'}

# extract all the key,value pairs
d.items()
# extract the keys
d.keys()
# extract the values
d.values()

def print_dictionary(d) :
    """print the elements of the dictionary"""
    for key in d :
        print key, d[key]

# Note that for key in d is the same as "for key in d.keys()"

def print_dictionary_sorted(d) :
    """print the elements of the dictionary in sorted order"""
    keys = d.keys()
    keys.sort()
    for key in keys :
        print key, d[key]


# Constructing using new instances of a class

# Each class has a method for constructing a new instance of the class
# that method has the name of the class, and is called a constructor.

# Examples:

s = str(1)
a = int(1)

# note:  you can do s = str() and a = int().  What do you think
# the result would be?

l = list()

# here are some of the way you can create the dictionary {"one": 2, "two": 3}

dict(one=2, two=3)
dict({'one': 2, 'two': 3})
dict(zip(('one', 'two'), (2, 3)))
dict([['two', 3], ['one', 2]])