type("What am I?")

"""
Explanation: Programming for Network Research
Erick Peirson, PhD | erick.peirson@asu.edu | @captbarberousse
Last updated 21 January, 2016

0. Introduction.
1. First steps with Python.
2. Objects and types.
3. Flow control: if, elif, else, and friends.* 4: Functions and functional programming.
5: I/O: working with data! Numpy and Pandas.
6: Our first tabular graph. Layout and visualization in Cytoscape.
7: Intro to NetworkX. GraphML. Advanced visualization in Cytoscape.
8: Blockmodels in NetworkX.
9: Properties of graphs: whole-graph statistics in NetworkX and Cytoscape.
10: Properties of nodes: centrality statistics.
11: ...

2. Objects and Types
We use all kinds of words to describe things in Python. We have strings (str), integers (int), lists (list), and functions (function). In the end, however, these are all objects. In fact, aside from syntax and operators (e.g. def, +, etc.), everything in Python is an object.
What sets different kinds of objects apart is their type. Every object has a type. You can find out the type of an object using the type function.
End of explanation
"""


print "I am an instance of a string"
print str                # <-- This is the str class, or type.
print type(str)          # <-- The str type is an instance of class `type`.

"""
Explanation: Another word for type is "class". When I'm working with a particular string (str), for example, I'm working with an instance of the str class.
End of explanation
"""


def myfunction():
    return True

type(myfunction)

"""
Explanation: A function is also an object, for which there is a function type.
End of explanation
"""


'a'/5

sum('what is the sum of this string')

"""
Explanation: 2.1. Be careful with your types!
Each type/class has different attributes, and different things that you can do with them. And many functions will work on instances of some types but not others. If you try to do something with an object of the wrong type, you'll usually get a TypeError.
End of explanation
"""


mystring = 'YYYYIKES'
print mystring.lower()    # lower() is an instance method for strings!

"""
Explanation: 2.2. Instance methods.
Instances (objects) of different classes have different built-in methods (another word for a function). An instance method is like a super power: think about it like an intrinsic ability of the object.
We can use instance methods by writing the name of the object, and then a period (.), and then the name of the method. For example, we can change a str to lower case using the lowercase() method:
End of explanation
"""


print mystring.count('Y')    # How many Y's are there in my string?

"""
Explanation: Just like other functions, instance methods can accept arguments. For example, I can count the number of 'Y' characters in my string object using the count() method:
End of explanation
"""


mystring.count(

"""
Explanation: In IPython, you can get information about a function or method by writing everything up to the open parens, and then pressing shift + tab. For example, try writing the following in the code-cell below:
python
mystring.count(
and then press shift + tab on your keyboard. It should open a little box with information about the method.
End of explanation
"""


class Car:
    """
    A small passenger automobile.
    """
    
    driver = None              # By default, there is no driver.
    number_of_passengers = 0   # By default, the car is empty.

"""
Explanation: 2.3. Make your own types!
It's easy to define your own types/classes in Python. Just use the class declaration.
Let's create a Car class. The Car class will define what attributes and methods an instance of a car will have.
End of explanation
"""


mycar = Car()

"""
Explanation: Let's unpack the code-block above.
On the first line, I declare that I am about to define a new class (or type) called Car.
python
class Car:
I then define two attributes, and give them default values. When we create a new car, it will have these attributes, and those attributes will have the specified default values.
python
driver = None              # By default, there is no driver.
number_of_passengers = 0   # By default, the car is empty.
Let's try instantiating our Car class. In other words, let's create a new car.
End of explanation
"""


print mycar.driver
print mycar.number_of_passengers

"""
Explanation: Sure enough, mycar has an attribute called driver, and another attribute called number_of_passengers.
End of explanation
"""


mycar.driver = 'John'
mycar.number_of_passengers += 5    # += means "increment by" -- in this case, we add 5 to the current value.

print mycar.driver
print mycar.number_of_passengers

"""
Explanation: You can change an attribute just like you would change a variable.
End of explanation
"""


class Car:
    """
    A small passenger automobile.
    """
    
    driver = None              # By default, there is no driver.
    number_of_passengers = 0   # By default, the car is empty.

    def honk(self):
        """
        What noise does a car make?
        """
        print 'beep beep!'
        
    def count_passengers(self):
        """
        Get the number of passengers in this car.
        """
        return self.number_of_passengers

"""
Explanation: Now let's give our Car class some methods. We'll redeclare the class, this time with methods.
End of explanation
"""


mycar = Car()    # Putting the parens () after the class name instantiates the class.
mycar.driver = 'John'
mycar.number_of_passengers += 5

print mycar.count_passengers()

"""
Explanation: This looks a lot like the function declarations that you saw earlier, but with two important differences:

They are part of the class definition for Car, so they are indented one extra level. 
Their first (and, in this case, only) parameter is something called self. 

self is how we refer to the instance of a class. When we create an instance of car, and then use that car instance's number_of_passengers method, the self in that function is the instance of the car that we're interacting with. 
So when I use the count_passengers method, and it returns self.number_of_passengers, it is retrieving the number_of_passengers attribute from the current car instance.
End of explanation
"""


mystring = '5'

mystring + 3.4

"""
Explanation: Pro tip: when defining a class, always include a docstring. That's the part with the triple open and close quotes and a descriptive message.
python
"""
I am a docstring. I describe what a class is all about.
"""
2.4. Changing the type of an object.
Sometimes it makes sense to transform an instance/object of one type into an instance/object of another type. For example, suppose I have a string that contains the character "5". 
python
mystring = '5'
If I want to do math with that 5 (say, I want to add 3.4 to it), I'll run into trouble:
End of explanation
"""


print type(mystring)    # mystring is a str
myint = int(mystring)   # But I can turn it into an int!

print type(myint)       # Yup, it's an int.
print myint + 3.4       # And now I can math!

"""
Explanation: But all is not lost! If I transform it into an int first, I'm golden.
End of explanation
"""


print dir("what does a string know?")

"""
Explanation: 2.5. Exploration
Frequently you will work with objects with which you are only partially familiar. Here are a few techniques that are helpful for learning about an object and its attributes and methods.
dir()
dir will attempt to display all of the attributes of an object.
End of explanation
"""


myCar = Car()
myCar.driver = 'Fido'

print myCar.__dict__

"""
Explanation: Notice that this includes methods, like count, title, and upper.
The __dict__ attribute.
Many objects can be represented as dictionaries (we haven't talked about that yet). For now, the important thing is that access the __dict__ attribute of most objects will display a list of non-function attributes as well as their values.
End of explanation
"""


myCar.    # Put your cursor right after the dot, and press ``tab``.

"""
Explanation: IPython: tab completion
In IPython, you can use tab completion! Just type the name of an object plus a period, and then press tab to see a list of attributes. You can start typing in part of an attribute name to limit the list.
End of explanation
"""


