((check this: 7/20/17 https://www.ibiblio.org/g2swap/byteofpython/read/oops.html ))

Object Oriented Programming

In all the programs we wrote till now, we have designed our program around functions i.e. blocks of statements to process data like using a calculator. This is called the procedure-oriented way of programming. There is another way of organizing your program which is to combine logically related data and functionality and wrap it inside something called an object. This is called the object oriented programming paradigm. Most of the time you can use procedural programming, but when writing large programs or have a problem that is better suited to this method, you can use object oriented programming techniques.

???#Ref http://www.diveintopython.net/object_oriented_framework/defining_classes.html ===???

Class and object

Classes and objects are the two main aspects of object oriented programming. 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.

What is a class? 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.

Too much dry talking, time for the real stuff:

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

Attributes defined in the class: including data attributes(variables) and function attributes, the functions in the class define corresponding methods of its instances, so function attributes are also called methods of the instances.

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:

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 this 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.

11.2 Initialize class, __init__(self) function

?? class method , instance method,

(( The init function really comes into play at this time. We create an instance of a class by first giving its name (in this case, Shape) and then, in brackets, the values to pass to the init function. The init function runs (using the parameters you gave it in brackets) and then spits out an instance of that class, which in this case is assigned to the name "rectangle".

In the same way that we used self to access functions and variables of the class instance from within itself, we use the name that we assigned to it now (rectangle) to access functions and variables of the class instance from outside of itself. Following on from the code we ran above, we would do this: Code Example 4 - accessing attributes from outside an instance

We aren't limited to a single instance of a class - we could have as many instances as we like. I could do this: Code Example 5 - More than one instance long_rectangle = Shape(120,10) fat_rectangle = Shape(130,120) Both long_rectangle and fat_rectangle have their own functions and variables contained inside them - they are totally independent of each other. There is no limit to the number of instances I could create.

))

(start)

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.

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.

Now let's jump right into OOP by making a simple class representing a dog:

Ex. 11.2-1 dog.py

# dog.py
1 class Dog:
2     def __init__(self):
3         self.color = 'clear'
4         self.eyeNumber = 0
5

>>> d1 = Dog( )      # Create a Dog object, d1
>>> d1.color                
'clear'
>>> d1.eyeNumber
0
>>> d1.color = 'yellow'      # assign new value to d1
>>> d1.eyeNumber = 4
>>> dl.color                    
'yellow'
>>> p1.eyeNumber
4             # Use your imagination, a yellow dog with 4 EYEs!
>>>

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, a new, initialized Dog instance can be obtained by d1 = Dog(), it has color 'clear' and 0 eyes when created. (A glass dog?)

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

Flexible constructor

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

In previous example, if we want to create a Dog object, d1, with a particular color and eye number, we must assign new value to d1 like d1.color ='yellow' and d1.eyeNumber=4 to make it like a real dog (Really? 4-eye-dog?).

A more convenient way to do the above is to pass information to the constructor __init__. Since constructors are just specially named functions, we can use parameters (as in ordinary functions) to provide the specific information.

In that case, arguments given to the class instantiation operator are passed on to the __init__(self, parameter1, parameter2).

For example: let's rewrite the previous Dog class to allow for a more flexible initialization:

Ex. 11.2-2 dog.py

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

# Now we can create a Dog object d2 and initializing it in a much simpler way:

d2 = Dog('black', 5)
d2.eyeNumber        # 5
d2.color            # black      

# A black 5-eye-dog, Ha! Anything is possible with Python

Here inside __init__() method, the initColor and initEye refer to the values passed into the constructor, and we assign those values to object variables(or instance variables) called color and eyeNumber using the self parameter, with the code

self.color = initColor self.eyeNumber = initEye

We create a new Dog object d2, and display it's color and eyeNumber, you can see the initialization function in action.

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.

We could have as many instances as we like.

d3 = Dog('white', 2 )
d4 = Dog('yellow', 1 )

Both d3 and d4 have their own functions and variables contained inside them - they are totally independent of each other.

11.3 add more methods to class

Same as __init__(self, otherParameters), all methods defined in a class that operate on objects of that class will have self as their first parameter. Again, this serves as reference to the object itself which in turn gives access to the state data inside the object. The self parameter is also a way for one function in the class to call another function in the class( and in the parent class, just wait and see).

Now let's add more methods to Dog class:

Ex. 11.3-1 dog.py

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

    def say(self):
        print( '"Woof, Woof, Woof!"' )

d = Dog('white', 2)
print('I'm a {0} dog, I have {1} eyes and I will say: ".format( d.color, d.eyeNumber ) )
d.say() 

# Run...
I'm a white dog, I have 2 eyes and I will say:
"Woof, Woof, Woof!"     # Finally! a normal dog!

(More on self)

When we create an object of a class, such as d, a Dog object, we refer to its variables or functions using objectName.attributesName such as d.color, d.eyeNumber and d.say() from outside of the class.

But when we are doing things inside a class, such as making a new function, if we need to refer to variables and other functions, we use self.attributesName.

self is how we refer to things in the class from within itself. self is the first parameter in any function defined inside a class.

To access these functions and variables elsewhere inside the class, their name must be preceeded with self (e.g. self.variableName).

See a variation of Dog class

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', 3 )
t.say()         
# run...
Woof, Woof, I have 3 eyes and my color is brown

Inside Dog class, in function say(), we access the variables color and eyeNumber using self.color and self.eyeNumber

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 Wallet class example (remember docstring?) :

Ex. 11.3-2 Wallet.py

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

    I earn 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 )
print( myWallet.earn(10) )
print( myWallet.earnTwice(20) )

# run...
5      # base money 5   
15     # total: 5 + 10
55     # total: 5 + 10 + 20 + 20

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

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 methods(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.(Python adds the self argument to the list for you)

Next similar example, I have a box of gumballs, I will put more gumballs into it. The numbers of gumballs are stored in a number list:

Ex. 11.3-3 box_gumballs.py

# box_gumballs.py

class Box:
    def __init__(self):
        self.numLst = []

    def add(self, x):
        self.numLst.append(x)
        return self.numLst

    def addTwice(self, x):
        self.add(x)
        self.add(x)
        return self.numLst

# run...
>>> b = Box()
>>> print(b.add(10))
[10]
>>> print(b.addTwice(15))
[10, 15, 15]

((??

Constructor with default values

We can also provide default values to the constructor: use __init__(self, initColor='clear', initEye=0) instead of the previous __init__. So if we make an instant of Dog and forget to provide arguments, say d5=Dog(), by default, d5.color has value 'clear' and d5.eyeNumber is 0. If we didn't forget, then the values provided will be assigned to object variables the same way as d2 = Dog('black',5) in previous example.

Another example to show how __init__(self, parameter=defaultValue) works:

Ex. 11.2-2 Robot.py

# Robot.py

class Robot:
    """ I'm a robot, depending on I have a name or not

    I can say hello with different message."""

    def __init___(self, name=None):
        self.name = name   

    def say_hello(self):
        if self.name:    # if name=None, returns False; if name has value, returns True
            print("Hi, I am {0}, what can I do for you?".format(self.name))
        else:
            print("Hi, I am a robot, I can not do anything for you before getting a name.")

>>> r = Robot()
>>> r.say_hello()
Hi, I am a robot, I can not do anything for you before getting a name.

>>> p = Robot('Buzz')
>>> p.say_hello()
HI, I am Buzz, what can I do for you?

))

11.4 Setter , Getter,

some standard methods, __str__()

, display(), __repr__(), ??property decorators?

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, Pet.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

Import a class

Use class in an different file, remember import?

Ideally, you should define your classes in separate file, then you should import them in your main program file using import statement.

If I put Pet class in a file named MyClass.py (module) together with some other classes I made. Now I open a new application to use the Person class. The Pet class will be an external class, which means Pet doesn’t exists within the confines of the current application file. So we need to import the module where Pet belongs to.

Ex. 11.4-2 PersonTest.py

# PetTest.py
import MyClass

P1 = MyClass.Pet( )
P2 = MyClass.Pet('Cream', 2)                 

print( P1.getName( ) )        # Buzz                 
print( P1.getAge( ) )         # 1                             

P1.setName( 'Bummbo' )
P1.setAge( 4 )

print( P1 )           # Bummbo is aged 4.                                            
print( P2 )           # Cream is aged 2.

P2.setName( 'Max' )
P2.setAge( 14 )

print( P2 )          # Max is aged 14.

11.5 Pro Topic

Instance variable and Class variable

http://www.diveintopython.net/object_oriented_framework/class_attributes.html

start) Class and Object(instance) Variables

The data part of classes and objects are also called fields. There are two types of fields - class variables and object variables(also called instance variables) which are classified depending on whether the class or the object owns the variables respectively. Actually, they are nothing but ordinary variables which are bound to the classes and objects namespaces.

Class variables are shared in the sense that they are accessed by all objects (instances) of that class. There is only copy of the class variable and when any one object makes a change to a class variable, the change is reflected in all the other instances as well.

Object variables(instance variables) are owned by each individual object/instance of the class, they are always defined as part of a method. In this case, each object has its own copy of the field i.e. they are not shared and are not related in any way to the field by the same name in a different instance of the same class.

An example will make this easy to understand.

# an example of class variable, instance variables
# Sheep
class Sheep:
    sheepCount = 0

    def __init__(self, color):
        self.color = color
        Sheep.sheepCount += 1

    def displayCount(self):
        print('Total sheep: ', Sheep.sheepCount )

    def displaySheep(self):
        print( "I'm a sheep, my color is ", self.color)        

s = Sheep('white')
s.displaySheep()       # I'm a sheep, my color is white (s calls it's method )
s.displayCount()       # Total sheep 1
print(s.sheepCount)    # 1 (instance s can access class variable, now we have only 1 sheep)
print( s.color )       # white (instance s accesses it's instance variable from outside the class)

t = Sheep('black')
t.displaySheep()       # I'm a sheep, my color is blsck ( t calls it's method)
t.displayCount()       # Total sheep 2

print(t.sheepCount)    # 2 (instance t also share class variable, now we have 2 sheep)
print(s.sheepCount)    # 2 (Now class variable, sheepCount has value 2 for all the instances)

print( Sheep.sheepCount)    # 2 (proves that sheepCount has value 2 from the class level)

Class variable In Sheep class, sheepCount is class variable, we refer to class variable using className.classVariable. So from inside and outside of the Sheep class, we access it via Sheep.sheepCount, such as inside the class, in methods __init__ and displayCount; Also from outside of the class, in the last line, we print out the value of the class variable using Sheep.sheepCount.

Class variable is shared by all the instances of the class. To prove this, we print out s.sheepCount(value 1) when there is only one instance is created; and after two instances are created, we print out t.sheepCount and s.sheepCount again, of cause, the last two have the same value 2. (they refer to the same class variable)

Instance variable Sheep class has only one instance variable defined in __init__ method, color. We refer to it inside the class using self.color and from outside of the class using instanceName.instanceVairable such as s.color or t.color

Note:

  • Using an underscore in front of an object variable is a common convention, such as _color, _age

  • Using instance variables is easier to maintain control over them by making sure the caller is providing the correct input

Instance variable has advantage We can write special setter methods to restrict the user's input:

Ex. 11.5-1 setter_limit.py


class Dog:
    def __init__(self, name=' ', age=0)
        self._name = name
        self._age = age

    def setAge(self, age):
        if 0<age<30:         # Put a reasonable limit on the input
            self._age = age
        else:
            print( "This input is not acceptable.")     

dog = Dog('Max', 10)
print(dog.name, dog.age)   # Max 10

dog.setAge(66)    # This input is not acceptable.
print(dog.age)    # 10

Instance variable _age, allows us to use setter method to control user's input, so that a value such as -10 or 300 won't be accepted as a normal dog's age.

Avoid class variables when you can, use instance variables instead .

Instance variables are owned by each individual object/instance of the class. In this case, each object has its own copy of the field i.e. they are not shared and are not related in any way to the field by the same name in a different instance.

Class variables are shared. When you make a change to a class variable (at the class level, can I say this?), that change will be seen by all the instances. (What if someone wants to cause problems on purpose?)

Class variable makes data global accessible, this may not be a good thing as you wanted.

Example:

class Greeting:
    time = 'morning'

    def say(self):
        print('Good '+ self.time )

g1 = Greeting()
g1.say()      # Good morning 

g1.time = 'evening'    # Change value different than the value you set when creating the class (still in object's level)
g1.say()      # Good evening (Still make sense)

g1.time = "2 o'clock"  
g1.say()      # Good 2 o'clock (Isn't it weird to hear that?) 

Greeting.time = 'badTime'   # Now I changed the class variable on class level

g2 = Greeting()   # Make a new object
g2.say()
Good badTime         # What is that?

Never use class variables to set default values to object variables. Use __init__ constructor code for that.

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

instance attributes and instance method

??? static method

class method

(( copy Class attributes are attributes that are set at the class-level, as opposed to the instance-level. Normal attributes are introduced in the init method, but some attributes of a class hold for all instances in all cases. For example, consider the following definition of a Car object:

class Car(object):

wheels = 4

def __init__(self, make, model):
    self.make = make
    self.model = model

mustang = Car('Ford', 'Mustang') print mustang.wheels

4

print Car.wheels

4

A Car always has four wheels, regardless of the make or model. Instance methods can access these attributes in the same way they access regular attributes: through self (i.e. self.wheels).

methods, though, called static methods, that don't have access to self. Just like class attributes, they are methods that work without requiring an instance to be present. Since instances are always referenced through self, static methods have no self parameter.

The following would be a valid static method on the Car class:

class Car(object): ... def make_car_sound(): print 'VRooooommmm!' No matter what kind of car we have, it always makes the same sound (or so I tell my ten month old daughter). To make it clear that this method should not receive the instance as the first parameter (i.e. self on "normal" methods), the @staticmethod decorator is used, turning our definition into:

class Car(object): ... @staticmethod def make_car_sound(): print 'VRooooommmm!'

))

=====

====

((old stuff??

>

b = Apple() # b is another object of Apple class

print(b.color, b.variety) # b gets its variables from Apple class, has values set as color 'red', variety 'Fuji' red, Fuji

b.color = 'green' # assign new value to b b.variety = 'Granny Smith'

print("b: I'm a {0} {1} apple, great for making apple pie.".format(b.color, b.variety)) b: I'm a green Granny Smith apple, great for making apple pie.

==

Another example to understand the instance variables: ex. add.py

>>> class Add:
    def AddThem (self, x = 0, y = 0):
        sum = x + y
           print ( 'The sum of {0} and {1} is {2}.'.format (x, y, sum))
...
>>> a = Add( )
>>> a.AddThem(4, 5)
The sum of 4 and 5 is 9.
>>>

In the above example, in the method AddThem, there are three instance variables. The input arguments, x and y are instance variables, they are given 0 as default values, if the caller forget to provide values, the method AddThem won't fail. Sum is the third instance variable.

=== from: https://en.wikibooks.org/wiki/A_Beginner%27s_Python_Tutorial/Classes#Creating_a_Class

Lingo[edit] Object-oriented programming has a set of lingo that is associated with it. It's about time that we have this all cleared up: when we first describe a class, we are defining it (like with functions) the ability to group similar functions and variables together is called encapsulation the word 'class' can be used when describing the code where the class is defined (like how a function is defined), and it can also refer to an instance of that class - this can get confusing, so make sure you know in which form we are talking about classes a variable inside a class is known as an 'attribute' a function inside a class is known as a 'method' a class is in the same category of things as variables, lists, dictionaries, etc. That is, they are objects a class is known as a 'data structure' - it holds data, and the methods to process that data.

==

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