from IPython.core.display import Image 
Image(filename='diagramFunction.png') 

"""
Explanation: Functions and Modules
Reference Document
<OL>
<LI> <A HREF="http://www.tutorialspoint.com/python/python_functions.htm">Python Functions</A>
<LI> <A HREF="http://en.wikibooks.org/wiki/Python_Programming/Functions">Python Programming/Functions</A>
<LI> <A HREF="http://www.tutorialspoint.com/python/python_modules.htm">Python Modules</A>
<LI> <A HREF="http://www.ibiblio.org/g2swap/byteofpython/read/making-modules.html">Making your Own Module</A>
</OL>

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
End of explanation
"""


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

type(1) == int

print addnums(0x1f,3.3)

print addnums("a","b")

print addnums("cat",23232)

"""
Explanation: An Example
End of explanation
"""


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

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


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

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

"""
Explanation: Python has it’s own local variables list. 
x is not modified globally
...unless you specify that it’s a global variable
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
All parameters (arguments) in the Python language are passed by reference. 
It means if you change what a parameter refers to within a function, the change also reflects back in the calling function.
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
"""


# %load "numfun1.py"
#!/usr/env python

"""
small demo of modules
"""
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


# %load "numfun1.py"
#!/usr/env python

"""
small demo of modules
"""
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


numfun1.numop1(2,3,2,greetings=None)

numop1(2,3,2,greetings=None)

# %load "numfun2.py"
#!/usr/env python

"""
small demo of modules
"""

print "numfun2 in the house"
x  = 2
s  = "spamm"

def numop2(x,y,multiplier=1.0,greetings="Thank you for your inquiry."):
    """ 
    Purpose: does a simple operation on two numbers.

    Input: We expect x,y are numbers
           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.

    Output: return x + y times the multiplier
    """ 
    if greetings is not None:
       print greetings
    return (x + y)*multiplier


print numfun2.x, numfun2.py

s = "eggs"
print s, numfun2.s

numfun2.s = 'jack'
print s, numfun2.s

"""
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
"""


from numfun2 import x, numop2

x == 2

numop2(2,3,2,greetings=None)

numfun2.s

numfun2.x

from numfun2 import s as my_fav_food
from numfun2 import numop2 as awesome_adder

print my_fav_food

print numfun2.s

awesome_adder(2,3,1)

import numfun2

from numfun2 import *

from numfun2 import x

"""
Explanation: Import Statements
End of explanation
"""


import datetime
y = datetime.datetime.now()
print y

x = datetime.datetime(2016,6,13)
y-x

help(datetime)

"""
Explanation: Built-In-Modules
<UL>
<LI> <B> sys:</B> exposes interpreter stuff & interactions
<LI> <B> os:</B> exposes platform-specific OS functions
<LI> <B> math:</B> basic mathematical functions & constants
<LI> <B> argparse:</B> command line parser
</UL>

Breakout Session: Exploring Some Modules
<OL>
<LI> Create and edit a new file called age.py
<LI> Within age.py, import the datetime module
     <UL>
     <LI> use datetime.datetime() to create a variable representing when you were born
     <LI> subtract the two, forming a new variable, which will be a datetime.timedelta() object. 
          Print that variable.
          <OL>
          <LI> How many days have you been alive? How many hours?
          <LI> What will be the date in 1000 days from now?
          </OL>
     </UL>
</OL>
End of explanation
"""


