Learning Python Programming - Part 6 - Classes in depth

in #programming8 years ago (edited)

Welcome to the 6th part of an ongoing series, Learning Python!

Today I will be going over classes more thoroughly.


source

What are classes?

Classes are probably the most powerful tool in programming. Classes contain a blueprint of an object. As with a blueprint, you can use it over and over to make similar objects.

I love making classes. They make programming so much simpler; to the point that it's only half programming and half scripting.

How do we start with a class?

class Dog:
    def __init__(self):
        pass

In this class definition, Dog can be any variable name, but it is common practice to capitalize the first letter and use only letters. Parenthesis after Dog are optional, and inside of them are where you would put another class object to inherit another class. The __init__ method is used to set class specific variables with its arguments and is generally used with every class you make, as that is one of the main advantages of classes.

Variables

Classes have their own variables. Some that are class specific, and others that are object specific.

Class specific

The class specific variables are shared by every object in that class and they can be changed on a class scale or on an individual scale. Class specific variables are defined above the __init__ method like this:

class Example:
    class_specific_variable = "I'm shared by every Example object"

This can be related to the assigning of "global" variables, or the variables you define by themselves.

When you change this variable on a class level, every object of this class will be changed. Using the code below, I will show some examples.

class Dog:
    says = "bark"
fido = Dog() 
pupper = Dog() 

Changing the variable at the class level:

Dog.says = "yip"
#fido.says = "yip"
# pupper.says = "yip"

Changing the variable at the object level:

fido.says = "yip"
# fido.says = "yip"
# pupper.says = "bark"

These class variables can be very helpful in making a default variable for every member of the class.

Object specific

The object specific variables can hold different values for every class instance and can only be changed on an individual scale. These variables are defined in the __init__ method and are defined with the prefix self.

class Example:
    def __init__(self):
        self.object_specific_variable = "I'm object specific"

The self prefix refers to the object in question and is used to denote that this variable can be specific to each object.

Self

The self prefix and the self variable are used in classes when referencing the object in question. Using the self prefix is just like using a class object name. Using the self variable is referencing the object in context. If you were to make a class that has a method to return the variable self, it would return the class object and the address from the stack proving that self points to the object in question. Here's the code for that proof if you'd like:

class Name:
    def returnself(self):
        return self
var = Name()
print(var.returnself())

Why do class methods have to have the first argument be self?

Class methods need the first argument to be self, so that they can use the object specific variables. (Not to say they can use class specific variables without it, because those need the self prefix also.) Also, they need it because when you call a class method you use the class object as a pointer to the class. That object doesn't get forgotten about, it goes into the first argument slot.

Inheritance

Classes are very powerful, but what if a class could be an extension of another class? That's exactly what inheritance is. With inheritance, you can make a sub-class of another class (which is referred to as a super-class) that will contain all the methods, class specific variables, and run the __init__ method from that super-class.
Here I show you inheritance with some aptly named classes:

class Super:
    class_var = 0
    class_var2 = 10
    def __init__(self):
        supers_var = 0

class Sub(Super):
    class_var2 = 100
    def __init__(self):
        pass

In that example the Sub class inherits the Super class.
What will print if we run "print(Sub().class_var)"?
A 0 will be printed because the Sub class inherited the variable definition.

The Sub class can also overwrite anything inherited from the Super class by defining what you want to overwrite in the Sub class.
What would print if we run "print(Sub().class_var2)"?
A 100 will be printed because the Sub class overwrote the Super's 10.

What would print if we run "print(Sub().supers_var)"?
A 0 will be printed because the Super's init method is run when it is inherited.

What if I want to inherit from multiple classes?
Why not? Just add a comma in the super list to inherit from more than one super class.

class Sub(Super0,Super1,Super2):
    def __init__(self):
        pass

What is inheritance good for?

Inheritance is good to use similarities between classes without rewriting code.

I learn best with examples, so let's check one out.

class WaterVessel:
    on_water = True

class Boat(WaterVessel):
    floats = True

class Submarine(Boat):
    in_water = True
    sinks = True

class Battleship(Boat):
    has_guns = True

class Aircraft:
    in_air = True
    on_land = True
    flies = True

class FloatPlane(Aircraft, Boat):
    on_land = False

What does each class hold?

WaterVessel:

  • on_water

Boat:

  • on_water
  • floats


source

Submarine:

  • on_water
  • floats
  • in_water
  • sinks


source

Battleship:

  • on_water
  • floats
  • has_guns


source

Aircraft:

  • in_air
  • on_land
  • flies


source

FloatPlane:

  • on_water
  • floats
  • in_air
  • flies
  • on_land = False


source

As you can see in this example, the FloatPlane is both a WaterVessel and an Aircraft, thusly it has all the corresponding variables and it overwrites the value on_land that Aircraft gives it because a FloatPlane can't land on land.

Inheritance is pretty cool and very useful too.

Classes with one object

Classes are very useful to make objects that can have their own values/variables. You can even make classes that will only have one object; this can be really helpful to make something that contains lots of values of different types with a very readable interface.