"""
Classes and objects
http://openbookproject.net/thinkcs/python/english2e/ch13.html
"""


# Let's write a class that represent the mathematical notion of
# a point in two dimensions.
# A point is associated with two coordinates, and let's call them
# x and y.
# We could just store the two values in a list or a tuple.
# But when we put them in an object we can also give the object
# methods that are appropriate for a two dimensional point.

# Here's the simplest way to define a class:

class Point :
    pass

# Let's create an instance of the class:

p = Point()

# we just called the constructor of the Point class.

# Attributes

# Attributes are the data that are associated with an object
# We can add/access attributes using the dot notation:

p.x = 3
p.y = 4

# We can now do things with objects of this class:

def distance_to_origin(p) :
    """compute the distance of a point from the origin"""
    import math
    return math.sqrt(p.x*p.x + p.y *p.y)

# now we can do:
print distance_to_origin(p)

# Issues:
# we want to be able to do p.distance() as opposed to calling it
# as a regular function, i.e. we want distance to be a method of Point.
# How do we make sure that a point has x and y attributes?

# Let's redefine the Point class:

import math
class Point :
    def __init__(self, x, y) :
        self.x = x
        self.y = y
    def distance_to_origin(self) :
        """compute the distance of a point from the origin"""
        return math.sqrt(self.x*self.x + self.y *self.y)
    def distance(self, other) :
        """compute the distance between a point object and another
        point object"""

        return math.sqrt((self.x - other.x)**2 +
                         (self.y - other.y)**2)

p1 = Point(1, 2)
p2 = Point(4, 6)
print p1.distance(p2)

# let's print the objects:
print p1
print p2
# the result is not very informative.
# let's fix that using the __str__ method:

import math
class Point :
    def __init__(self, x, y) :
        self.x = x
        self.y = y
    def distance_to_origin(self) :
        """compute the distance of a point from the origin"""
        return math.sqrt(self.x*self.x + self.y *self.y)
    def distance(self, other) :
        """compute the distance between a point object and another
        point object"""

        return math.sqrt((self.x - other.x)**2 +
                         (self.y - other.y)**2)
    def __str__(self) :
        return "Point object with x = %d, y = %d" % (self.x, self.y)

p1 = Point(1, 2)
p2 = Point(4, 6)
# printing the ojbects produces a more informative result:
print p1
print p2
# As a rule, whenever you write a classe you should also write
# an __str__ method - it's very useful for debugging!