===save for References, old version of Class/Objects

https://www.tutorialspoint.com/python/python_classes_objects.htm

setter and getter

https://www.python-course.eu/python3_properties.php

http://www.diveintopython3.net/iterators.html#divingin

https://jeffknupp.com/blog/2014/06/18/improve-your-python-python-classes-and-object-oriented-programming/

http://www2.lib.uchicago.edu/keith/courses/python/class/5/#custom

===

Understanding Class and Object

Classes and objects are the two main aspects of Object Oriented Programming System (OOPs).

A class creates a new type where objects are instances of the class.

Essentially, an object is a collection of data and functions that operate on that data.

You can think of class as a blueprint, it simply describes how to make something. You can create lots of objects of a particular kind from that blueprint, technically called instances of that class.

The syntax of defining a class is semiller to a function:

class MyClassName:

    data attributes...

    functions...

I define a class using the class keyword (similar to function definition)

The first line defines the class container, which starts with the keyword class and the class name, then a colon : ends the line.

All indented statements(4 spaces) form the body of the class, it's called the class suite. The class suite consists of all the component statements defining class members, data attributes(variables) and functions.

  • Class names are usually capitalized, MyClassNameLikeThis, but this is only a convention, not a requirement
  • Everything in a class is indented, just like the code within a function, if statement, for loop. The first thing not indented is not in the class.

Class instantiation uses function notation. We create an object/instance of the class using the name of the class followed by a pair of parentheses.

Using the class to create an object:

x = MyClassName()

creates a new instance of the class and assigns this object to the local variable x. For now, just pretend that the class object is a parameterless function that returns a new instance/object of the class.

x, as an instance of the class will get all the attributes defined in the class. Accessing the attributes of x using dot notation: x.attribute

See a very simple class example:

Apple class Let's make a simple class to represent an apple, this class has only one variable color:

Ex. 11.1-1 apple.py

# apple.py
class Apple:           
    color = 'red'

The first line defines the class Apple, in the indented class suite, there is only a class variable named color, which is set to a value of 'red'.

When creating a class Apple, I haven't actually created an apple object. Instead, what I've created is a sort of instruction manual for constructing "apple" objects.

Next, let's create an object using class Apple

a1 = Apple()         
print( a1.color )     # red 

a1.color = 'green'    # assign a new value
print( a1.color)      # green

We create an Apple object, a1 by calling the class name with an parenthesis(it's empty in this example, just pretend it's a function with no input parameters). a1 as an instance of Apple class, it has attribute, color that starts at value 'red', we can access it by using instanceName.attribute, here it's a1.color; we can also assign new value to the attributes.

Now, on top of the above example I will add a function say() to Apple class:

Functions defined in a class

they always takes at least one argument (also the first argument), self, which refers to the object itself being created. The use of self is a universal convention in Python. (some other languages, such as Java and C++, require to use the name this).

Ex. 11.1-2 apple.py

# apple.py
class Apple:           
    color = 'red'

    def say(self):
        print("An apple a day keeps the doctor away!")

a2 = Apple()          # a2 is an object of Apple class
print( a2.color)      # red

a2.say()              # An apple a day keeps the doctor away!

a2 is Apple object, it's a instance of Apple class, it has a variable color and method say() defined in Apple class. We access them using a2.color and a2.say()

About self

Class methods have only one specific difference from ordinary functions - they must have self as the first parameter in the parameter list, this particular variable refers to the object itself. The use of self is a universal convention in Python. (some other languages, such as Java and C++, require to use the name this).

When you call the method you do not give a value for self parameter, Python will provide it.

If the method has no other input arguments, then it's defined with only self parameter, like say(self) in Apple class. When it's called, the self is omitted so the parenthesis is empty as a2.say()

We will talk more about self later.

Conclusion

Now it's not too difficult to understand:

An object combines variables and functions into a single entity. Objects get their variables and functions from classes. Classes are essentially templates for creating your objects.

Variables that belong to an object or class are referred to as data attributes ( also called fields).

Objects can also have functionality by using functions that belong to a class. Such functions are called methods. Simply put, methods are functions that “belong to” an object. These functions are defined inside a class, they always have the first parameter self referring to the object itself.

This terminology is important because it helps us to differentiate between functions and variables which are independent and those which belong to a class or object.

(??? question?? in class, say() no self in it, then a1.say() error message; Apple.say() is ok

Do I have to put self in functions for objects? (method?) why?? later, use @staticmethod, then a.say() is fine as Apple.say() or use self in say(self), a.say() is fine )

11.2 Initialize class, __init__() function

In previous Apple class, it looks like we set the object's value by assigning values to the class variables(color = 'red') when defining the class. But in fact, the standard way to initialize an object's values when creating objects is to use __init__( ) function in classes.

__init__() is a special method, which is called class constructor or initialization method that Python calls automatically when you create a new instance of the class.

__init__ is one of the many method names that have special significance in Python classes. __init__ is short for "initialize". Notice the double underscore both in the beginning and at the end in the name.

All classes should have an __init__() method as constructor, when you create a class without a constructor, Python automatically creates a default constructor for you that doesn't do anything.

Like all methods in a class, __init__() always takes at least one argument (also the first argument), self, which refers to the object itself being created.

Let's rewrite the Apple class in a more standard way:

class Apple:
    def __init__(self)
        self.color = 'red'

    def say(self):
        print("I'm an apple.")

a1 = Apple()          # create an Apple instance, a1
print(a1.color)      # red
a1.say()            # I'm an apple

Remember that self in a Python class refers to the object itself being created. In the constructor method, __init__, we define a variable using self.color to indicate that color is instance variable, it belongs to an object.

When a class defines an __init__() method, class instantiation automatically invokes __init__() for the newly-created class instance, that means when you create a class instance (an object), Python automatically calls __init__function only once to initializing an object's variables.

So in this example, a1 is a new, initialized Apple instance, it has color 'red' and a method a1.say() that can print out message "I'm an apple."

Flexible constructor We can make our class constructor more general by putting extra parameters into it, use __init__(self, parameter1, parameter2...) for greater flexibility.

class Apple:
    def __init__(self, initColor)
        self.color = initColor

    def say(self):
        print("I'm an apple.")

a2 = Apple('red')          
print(a2.color)      # red
a2.say()            # I'm an apple

a3 = Apple('green')
print(a3.color)      # green

Here inside __init__() method, the initColor refers to the values passed into the constructor, and we assign the value to instance variable using the self parameter, with the code

self.color = initColor

You can see the initialization function in action when accessing the attributes of objects a2, a3

Attention! notice that we do not explicitly call the __init__ method but pass the arguments in the parentheses following the class name when creating a new instance of the class. This is the special significance of this method.

(More on self)

When we create an object of a class, such as a2, an Apple object, from outside of the class, we refer to its variables or methods using objectName.attributesName such as a2.color, a2.say()

But when we are doing things inside a class, such as using the instance variables or calling other functions defined in the class, we use self.attributesName.

To be simple, self is how we refer to things in the class from within itself.

Keep changing Apple class:

class Apple:
    def __init__(self, initColor)
        self.color = initColor

    def say(self):
        print("I'm an apple, my color is ", self.color)

a4 = Apple('red')          
print(a4.color)      # red
a4.say()            # I'm an apple, my color is red

a5 = Apple('pink')
print(a5.color)      # pink
a5.say()            # I'm an apple, my color is pink

In the new method say(), we access the instance variable color using code self.color

Let's see another Dog class to understand all the above again.

class Dog:
    def __init__(self, color, eyeNumber):
        self.color = color
        self.eyeNumber = eyeNumber

    def say(self):
        print( 'Woof, Woof, I have ', self.eyeNumber, ' eyes and my color is ', self.color )

t = Dog('brown', 1 )
print(t.color)    # brown
t.say()          # Woof, Woof, I have 1 eyes and my color is brown 

d = Dog('white', 2 )  
print(d.eyeNumber)     # 2
d.say()                # Woof, Woof, I have 2 eyes and my color is white

In function say(), we access the instance variables color and eyeNumber using self.color and self.eyeNumber.

t,d are all instances of Dog class, they all have the variables and methods defined in the class, class is the blueprint of creating these objects.

Input argument and instance variable have the same name, but they are different variables

Noticed? Here, in the header line we define the __init__ method as taking input parameters color and eyeNumber (along with the usual self).

Inside __init__, we just create two new instance variables (fields) also called color and eyeNumber using code self.color self.eyeNumber.

The self indicates the object itself, the dotted notation self.variable allows us to differentiate between the two different kinds of variables: the input parameters and the instance variables. It's common to use the same name for them.

Function calls other function inside the class:

We will use an example to show how to access other methods inside the same class, again, self is how we refer to things in the class from within itself (such as self.method).

See example Wallet class (remember docstring?) :

Ex. 11.3-2 Wallet.py

# Wallet.py
class Wallet:
    """I have some base money in my Wallet.

    I earn more and more money to put in my Wallet."""

    def __init__(self, x = 0):
        self.total = x

    def earn(self, y = 0):
        self.total += y
        return self.total

    def earnTwice(self, z = 0):
        self.earn(z)
        self.earn(z)
        return self.total

myWallet = Wallet(5)
print( myWallet.total )        # 5 (base money)
print( myWallet.earn(10) )      # 15 (total: 5 + 10 )
print( myWallet.earnTwice(20) )   # 55 (total: 5+10+20+20 )

Here in Wallet class, in the constructor, we define object variable total and assign a value x to it as the base money.

Then we define the second method earn(), in earn(), we refer to object variable total using self.total.

The same thing, in the third method addTwice(), we may call other method (earn()) by using method attributes of the self argument, self.earn(). When we call the methods, we don't need to include the self argument, only provide value z for other input argument.

Constructor with default values

Did you notice that in Wallet class, the constructor __init__(self, x = 0) is a little different? We set a default value x=0 here. So if we make an instant of Wallet and forget to provide value for x argument like w = Wallet(), then w will have 0 as the base money. If we didn't forget, then the values provided will be assigned to object variable, myWallet = Wallet(5), so myWallet has base money 5.

The previous Apple and Dog class can have alternative constructors that have default values:

class Apple:
    def __init__(self, initColor = 'white')
        self.color = initColor

ap1 = Apple()      
print( ap1.color )      # white (default value is working)

a2 = Apple('green')
print( a2.color )       # green
class Dog:
    def __init__(self, color = 'clear', eyeNumber = 0):
        self.color = color
        self.eyeNumber = eyeNumber

d1 = Dog()
print( d1.color, d1.eyeNumber )   # clear, 0 ( a glass dog with no eyes! Any thing is possible in Python )

d2 = Dog('red', 5)
print(d2.color, d2.eyeNumber)   # red, 5  (Dose Clifford have 5 eyes?)

===== end

setter, getter

Setter and getter methods are often used in most classes. The setter methods are for changing the data. The getter methods do not change the values of attributes, they just return the values. By doing these, the attributes of a class are made private to hide and protect them from other code. This is called encapsulation.

We will make a Pet class, and add setter, getter methods to it:

Ex. 11.4-1 Pet.py

# Pet.py
class Pet:
    def __init__(self, name=’Buzz’, age=1):
        self.name = name
        self.age = age

    def setName(self, name):
        self.name = name
    def setAge(self, age):
        self.age = age

    def getName(self, name):
        return self.name
    def getAge(self, age):
        return self.age

    def __str__ (self):
        return “ %s is aged %d.” % (self.name, self.age)

The above example, the Pet class begins with the standard constructor __init__ function, when we create a Pet object, it’s two instance variables: name and age will be initialized. If the user fails to provide these values, they default to ‘Buzz’ and 1.

The setName( ) and setAge( ) are setters, getName( ) and getAge( ) are getters, they are the two types of accessors, provide write-only or read-only access to the underlying value.

Use setter and getter Ex. 11.4-1 continued, Person.py

# use Pet

p1 = Pet()
print( p1.getName, p1.getAge )   # Buzz 1 

p1.setName('Sniffy')
p1.setAge(3.5)
print( p1.getName, p1.getAge )   # Sniffy 3.5

print( p1 )     # Sniffy is aged 3.5.

Standard method, __str__()

The last line of code in previous lesson: print( p1 ) prints out a message: Sniffy is aged 3.5

This envolved the last method we defined in Pet class, the __str__() method, look at it's name: the same naming convention as the constructor __init__, there are two underscores before and after the name. It is common that Python uses this naming technique for special methods.

So, you guessed right, just like __init__(), __str__ is another one of the many method names that have special significance in Python classes.

__str__( ) method is responsible for returning a string representation as defined by the class creator. It is a commonly used method in about every class for the users to be able to print the object.

It is required that the __str__ method create and return a string. print automatically use this when printing the objects.

(( another Dress class to demo how to use setter, getter and __str__))

class Dress:
    def __init__(self, color='white', size=0):
        self.color = color
        self.size = size
    def setColor(self, color):
        self.color = color
    def setSize(self, size):
        self.size = size
    def getColor(self)
        return self.color
    def getSize(self):
        return self.size

    def __str__(self):
        return "%s dress, size %d" % (self.color, self.size)

d1 = Dress()
print(d1)    # white dress, size 0

d1.setColor('blue')
d1.setSize(8)
print(d1)   # blue dress, size 8

d2 = Dress('purple', 5)
print(d2)   # purple dress, size 5

d2.setSize(2)
print( d2.getSize() )    # 2
print(d2)    # purple dress, size 2

See different ways to assign values to an instance.

d1 has default values at initialization (color 'white' and size 0), then through the setter, d1 gets new values (color 'blue' and size 8)

d2 got it's initialization values color 'purple' and size 5 when it's created; later it's size is changed to be 2 via the setter method.

__str__ returns a string for print(object) to use to print out message purple dress, size 2

inheritance

In Object Oriented Programming(OOP), a great benefit is to reuse code. Inheritance is one way to achieve this.

Inheritance enables us to make new class based on an existing class. The new class(child class or subclass) will inherit all the attributes of the existing class(parent class or superclass), and on top of that, a child class can also override attributes from the parent class or have extra features(?methods? fields?) of its own.

12.1 Parent and Child Class

Syntax Child class is declared as usual class, only has a parentheses after it's name with Parent class name inside:

class Parent:
    code...

class Child(Parent):
    code...

Let's see an example of a parent class FarmAnimal, a child class Duck to show how inheritance works:

class FarmAnimal:
    def __init__(self, type='Animal', legNumber=0):
        self.type = type
        self.legNumber = legNumber

    def setLeg(self, legNumber):
        self.legNumber = legNumber
    def getLeg(self):
        return self.legNumber

    def describe(self):
       print( "%s has %d legs, it lives on the farm." %(self.type, self.legNumber) )

class Duck(FarmAnimal):
    def makeSound(self):
        print("Quark! Quark! Quark!")

In class FarmAnimal, we defined a constructor (two instance variables type and legNumber ), a setter and a getter methods (setLeg and getLeg) and another method ( describe() ) to show information about the object.

Instead of starting from scratch, another class Duck is derived from a preexisting class FarmAnimal by putting the parent class name in parentheses after the new class name ( Duck(FarmAnimal) ) in the header line.

Duck is a child class of FarmAnimal, it will inherent all the attributes of it's parent class FarmAnimal. Let's see:

Continue with previous codes

duck = Duck()     # create a Duck instance 
print( duck.legNumber )   # 0 (variable defined in parent class)
duck.setLeg(2)    # access parent method
print( duck.legNumber )   # 2

duck2 = Duck('Duck', 2)   # another Duckl instance ( access the constructor in parent class)
duck2.describe()   # Duck has 2 legs, it lives on the farm. (access parent class method)

duck2.makeSound()   # Quark! Quark! Quark! (access method in child class)

From the above application of parent, child classes, we can see that the child class Duck inherit all the attributes defined in the parent class, it has the constructor, the setLeg() and getLeg() and describe() methods; it also has it's own method makeSound().

The child class inherits the attributes of its parent class, and you can use those attributes as if they were defined in the child class.

Overriding methods

A child class can also override data members and methods from the parent.

Let's continue with the example in previous lesson, make another child class of the parent class FarmAnimal, name it Cow

# continue with `FarmAnimal` class

class Cow(FarmAnimal):
    def __init__(self, legNumber = 4)
        self.type = 'Cow'
        self.legNumber = legNumber

    def makeSound(self):
        print("Moo! Moo! Moo!")

    def getLeg(self):
        if self.legNumber != 4:
            return "Holy Cow! A cow with %d legs!" % (self.legNumber)
        else:
            return self.legNumber

# Use the class
cow = Cow()      # create an instance of Cow (invoke the constructor in Cow, it override the parent class constructor)

print(cow.legNumber)    # 4 (child class constructor sets the default value)

cow.describe()    # Cow has 4 legs, it lives on the farm.

cow.setLeg(2)      # access setter method defined in the parent class

print( cow.getLeg() )    # Holy Cow! A cow with 2 legs!

cow2 = Cow(3)    # create another instance (again invoke the child class constructor)
cow2.describe()  # Cow has 3 legs, it lives on the farm.

cow2.makeSound()   # Moo! Moo! Moo!  (method of it's own)

From the above example, you can see, the constructor in child class Cow override the constructor in parent class FarmAnimal, so when creating Cow instances, cow and cow2 get their initial state values from the child constructor;

Cow also overrides method getLet(), which prints out a message when letNumber is not 4.

Cow inherits all other methods from the parent class, setLeg(), describe();

Cow defines it's own method makeSound() to print out message of a cow's sound "Moo! Moo! Moo!"

Pet and Cat classes

See another inheritance example for better understanding: Pet and Cat classes.

Pet class is the parent class, it has 4 fields: name, color, type, and age.

Cat is a child class that derived from Pet class, inherit the 4 fields from Pet, but they both have some new feature of their own.

So Cat and Kitten are all Pets. They all inherit the 4 fields from Pet, but they both have some new feature of their own.

class Pet:
    def __init__ (self, n=' ', c=' ', t=' ', a=0):
        self.name = n
        self.color = c
        self.type = t 
        self.age = a

    def setName (self, n ):
        self.name = n
    def getName (self):
        return self.name
    def setColor (self, c ):
        self.color = c
    def getColor (self):
        return self.age
    def setType (self, t ):
        self.type = t
    def getType (self):
        return self.type
    def setAge (self, a)
        self.age = a
    def getAge (self):
        return  "{0} years".format(self.age) 

    def __str__ (self):
        return "{0} is a {1} {2} aged {3} years.".format (self.name, self.color, self.type, self.age)

# Cat is a child class, Pet is the parent class

class Cat( Pet ):       
    def __init__ (self, n=' ', c=' ', a=0):
        self.name = n
        self.color = c
        self.type = 'cat'
        self.age = a

     def setType (self, t)
        print ('{0} refuses to change type, it will always be a {1} in this life.'. format (self.name, self.type) )

     def setAge(self, a):
         if 1<= a <30:
             self.age = a
         else:
             print("That's not a fit number for a cat's age.")

Pet class has four things common to all pets: name, color, type and age. The code also has setters and getters (accessors). At the end the __str__( ) method can print a simple message about this pet’s name, age, type and color.

Cat is subclass of Pet.

Cat class constructor accepts only name, color and age, it’s type is fixed to be 'cat'. The new constructor overrides the Pet constructor. Cat also overrides the setter setType( ). If someone tries to change the Cat type, he will not be able to change it, instead he will get a message “…refuses to change type, it will always be a cat in this life!”.

'Cat' also overrides setAge() method, it puts a limit on the age to be 1<age<30 (in years).

====

results matching ""

    No results matching ""