{1,2,3,"bingo"} 

type({1,2,3,"bingo"}) 

type({})

type(set())

set("spamIam")

"""
Explanation: More Data Structures, Control Statements, <BR> Functions, and Modules
Sets
End of explanation
"""


a = set("sp"); b = set("am"); print(a) ; print(b) 

c = set(["a","m"])

c == b

"p" in a

"ps" in a

q = set("spamIam")
a.issubset(q)

a | b

q - (a | b)

q & (a | b)

"""
Explanation: Sets have unique elements. They can be compared, differenced, unionized, etc.
End of explanation
"""


# this is pretty volitile...wont be the same
# order on all machines
for i in q & (a | b):
    print(i)

q.remove("a")

q.pop()

print(q.pop())
print(q.pop())

print(q.pop())

# q.pop()

"""
Explanation: Like lists, we can use as (unordered) buckets
.pop() gives us a random element
End of explanation
"""


d = {"favorite cat": None, "favorite spam": "all"}

"""
Explanation: &nbsp;
Dictionaries
denoted with a curly braces and colons
End of explanation
"""


print(d["favorite cat"])
d[0]   ## this is not a list and you dont have a keyword = 0

e = {"favorite cat": None, "favorite spam": "all", \
     1: 'loneliest number'}
e[1] == 'loneliest number'

"""
Explanation: these are key: value, key: value, ...
End of explanation
"""


# number 1...you've seen this
d = {"favorite cat": None, "favorite spam": "all"}

# number 2
d = dict(one = 1, two=2,cat = 'dog') ; print(d)

# number 3 ... just start filling in items/keys
d = {}  # empty dictionary
d['cat'] = 'dog'
d['one'] = 1
d['two'] = 2
d

# number 4... start with a list of tuples
mylist = [("cat","dog"), ("one",1),("two",2)]
print(dict(mylist))

dict(mylist) == d

"""
Explanation: dictionaries are UNORDERED<sup>*</sup>.  

You cannot assume that one key comes before or after another

<sup>*</sup> you can use a special type of ordered dict if you really need it:
http://docs.python.org/whatsnew/2.7.html#pep-372-adding-an-ordered-dictionary-to-collections
4 ways to make a Dictionary
End of explanation
"""


d = {"favorite cat": None, "favorite spam": "all"}

d = {'favorites': {'cat': None, 'spam': 'all'}, \
     'least favorite': {'cat': 'all', 'spam': None}}
print(d['least favorite']['cat'])

"""
Explanation: &nbsp;
Dictionaries: they can be complicated (in a good way)
End of explanation
"""


phone_numbers = {'family': [('mom','642-2322'),('dad','534-2311')],\
                     'friends': [('Sylvia','652-2212')]}

for group_type in ['friends','family']:
        print("Group " + group_type + ":")
        for info in phone_numbers[group_type]:
             print(" ",info[0], info[1])

# this will return a list, but you dont know in what order! 
phone_numbers.keys()

phone_numbers.values()

"""
Explanation: remember: the backslash () allows you to across break lines. Not technically needed when defining a dictionary or list
End of explanation
"""


for group_type in phone_numbers.keys():
        print("Group " + group_type + ":")
        for info in phone_numbers[group_type]:
             print(" ",info[0], info[1])

"""
Explanation: &nbsp;
.keys() and .values(): are called methods on dictionaries
End of explanation
"""


groups = phone_numbers.keys()
groups.sort()
for group_type in groups:
        print("Group " + group_type + ":")
        for info in phone_numbers[group_type]:
             print(" ",info[0], info[1])

"""
Explanation: we cannot ensure ordering here of the groups
End of explanation
"""


for group_type, vals in phone_numbers.iteritems():
        print("Group " + group_type + ":")
        for info in vals:
             print(" ",info[0], info[1])

"""
Explanation: .iteritems() is a handy method,
returning key,value pairs with each iteration
End of explanation
"""


phone_numbers['co-workers']

phone_numbers.has_key('co-workers')

print phone_numbers.get('co-workers')

phone_numbers.get('friends') == phone_numbers['friends']

print phone_numbers.get('co-workers',"all alone")

"""
Explanation: Some examples of getting values:
End of explanation
"""


# add to the friends list
phone_numbers['friends'].append(("Jeremy","232-1121"))
print(phone_numbers)

## Sylvia's number changed
phone_numbers['friends'][0][1] = "532-1521"

phone_numbers['friends'][0] = ("Sylvia","232-1521"); 
print(phone_numbers['friends'])

## I lost all my friends preparing for this Python class
phone_numbers['friends'] = [] # sets this to an empty list

## remove the friends key altogether
print(phone_numbers.pop('friends'))

print(phone_numbers)

del phone_numbers['family']

print(phone_numbers)

"""
Explanation: &nbsp;
&nbsp;
setting values
you can edit the values of keys and also .pop() & del to remove certain keys
End of explanation
"""


phone_numbers.update({"friends": [("Sylvia's friend, Dave", "532-1521")]})
print(phone_numbers)

"""
Explanation: &nbsp;
.update() method is very handy, like .append() for lists
End of explanation
"""


x = 1
    print(x)

"""
Explanation: &nbsp; 
Loops and branches in python
Python has the usual control flow statements:
- if, else, elif
- for loops
- while loops
- break, continue, pass
Indentation in Python defines where blocks begin and end.
End of explanation
"""


# You can mix indentations between different blocks ... 
# but this is ugly and people will judge you

x = 1
if x > 0:
    print("yo")
else:
        print("dude")

# You can put everything on one line/statement

print("yo" if x > 0 else "dude")

# Multiple cases

x = 1
if x < -10:
    print("yo")
elif x > 10:             # 'elif' is short for 'else if'
    print("dude")
else:
    print("sup")

for x in range(5):
    print(x**2)

for x in ("all","we","wanna","do","is","eat","your","brains"):
    print(x)

x = 0
while x < 5:
    print(pow(2,x))
    x += 1             # don't forget to increment x!

# Multiple levels
for x in range(1,5):
    if x % 2 == 0:
        print(str(x) + " is even.")
    else:
        print(str(x) + " is odd.")

# Blocks cannot be empty

x = "fried goldfish"
if x == "spam for dinner":
    print("I will destroy the universe")
else:
    AA=1
    # Nothing here.

# Use a 'pass' statement, which indicates 'do nothing'

x = "fried goldfish"
if x == "spam for dinner":
    print("I will destroy the universe")
else:
    pass

# Use a 'break' statement to escape a loop

x = 0
while True:
    print(x**2)
    if x**2 > 10:
        break
    x +=1

"""
Explanation: IPython Notebook automatically converts tabs into spaces, but some programs do not. Be careful not to mix these up! Be consistent in your programming.
If you're working within the Python interpreter (not the IPython Notebook), you'll see this:
&gt;&gt;&gt; x = 1
&gt;&gt;&gt; if x &gt; 0:
...     print "yo"
... else:
...     print "dude"
... print "ok"
...
yo
ok
End of explanation
"""


def addnums(x,y):
    return x + y

addnums(2,3)

print(addnums(0x1f,3.3))

print addnums("a","b")

print addnums("cat",23232)

"""
Explanation: What is a Function?
<UL>
<LI> A block of organized, reusable code that is used to perform a single, related action.
<LI> Provides better modularity for your application and a high degree of code reusing.
<LI> You can name a function anything you want as long as it:
    <OL>
    <LI> Contains only numbers, letters, underscore
    <LI> Does not start with a number
    <LI> Is not the same name as a built-in function (like print).
    </OL>
</UL>

Basic Synthax of a Function
An Example
End of explanation
"""


def numop(x,y):
    x *= 3.14
    return x + y
x = 2
print(numop(x, 8))
print(x)

def numop(x,y):
    x *= 3.14
    global a
    a += 1
    return x + y, a

a = 2
numop(1,1)
numop(1,1)

"""
Explanation: Scope of a Function
End of explanation
"""


def changeme_1( mylist ):
   mylist = [1,2,3,4]; # This would assig new reference in mylist
   print( "Values inside the function changeme_1: ", mylist)
   return

def changeme_2( mylist ):
   mylist.append([1,2,3,4]);
   print( "Values inside the function changeme_2: ", mylist)
   return

mylist1 = [10,20,30];
changeme_1( mylist1 );
print( "Values outside the function: ", mylist1)
print

mylist2 = [10,20,30];
changeme_2( mylist2 );
print( "Values outside the function: ", mylist2)

"""
Explanation: Pass by reference vs value
End of explanation
"""


def numop1(x,y,multiplier=1.0,greetings="Thank you for your inquiry."):
    """ numop1 -- this does a simple operation on two numbers.
     We expect x,y are numbers and return x + y times the multiplier
     multiplier is also a number (a float is preferred) and is optional.
    It defaults to 1.0.
     You can also specify a small greeting as a string. """
    if greetings is not None:
       print(greetings)
    return (x + y)*multiplier

help(numop1)

numop1(1,1)

numop1(1,1,multiplier=-0.5,greetings=None)

"""
Explanation: Function Arguments
You can call a function by using the following types of formal arguments:
<UL>
<LI> Required arguments (arguments passed to a function in correct positional order. Here, the number of arguments in the function call should match exactly with the function definition)
<LI> Keyword arguments (identified by parameter names)
<LI> Default arguments (assume default values if values are not provided in the function call for those arguments) 
<LI> Variable-length arguments (are not explicitly named in the function definition)
</UL>

Keyword Arguments
End of explanation
"""


def cheeseshop(kind, *arguments, **keywords): 
    print( "-- Do you have any", kind, "?")
    print( "-- I'm sorry, we're all out of", kind )
    for arg in arguments: 
        print( arg)
    print ("-" * 40)
    keys = keywords.keys()
    keys.sort()
    for kw in keys: 
        print (kw, ":", keywords[kw])

cheeseshop("Limburger", 
           "It's very runny, sir.", 
           "It's really very, VERY runny, sir.",
           shopkeeper='Michael Palin',
           client="John Cleese",
           sketch="Cheese Shop Sketch")

"""
Explanation: Unspecified args and keywords
End of explanation
"""


import math

math.cos(0)

math.cos(math.pi)

math.sqrt(4)

from datetime import datetime

now = datetime.now()

print(now.year, now.month, now.day)

from math import acos as arccos

arccos(1)

"""
Explanation: What is a Module?
<UL>
<LI> A Python object with arbitrarily named attributes that you can bind and reference.
<LI> A file consisting of Python code.
<LI> Allows you to logically organize your Python code.
<LI> Makes the code easier to understand and use.
<LI> Can define functions, classes and variables. 
<LI> Can also include runnable code.
</UL>
<B> Any file ending in .py is treated as a module. </B>
End of explanation
"""


