Programmer's Python - Class & Type
Written by Mike James   
Monday, 08 February 2021
Article Index
Programmer's Python - Class & Type
Class Based Type
Hierachical Typing

Hierarchical Typing

Things are a little more complicated in most class-based languages that implement hierarchical type. In this case inheritance can be used to create a type hierarchy. 

For example:

Class MyClassB:inherits MyClassA{
 lots of properties
}

Now MyClassB has all of the properties of MyClassA plus whatever is added as part of its definition. 

MyClassB is said to be a subclass of MyClassA and as it has all of the properties of MyClassA you can use it anywhere you could use an instance of MyClassA. 

After all, typing is all about making sure that an object has the properties that you are using, and an instance of MyClassB has all of the properties of MyClassA and so it can be treated as such. MyClassB is also a subtype of MyClassA in the sense that it is also a MyClassA as well as being a new type all of its own.

So it is perfectly OK in most strongly-typed class-based languages to write things like:

MyClassA myObject=new MyClassB();

and then proceed to use any properties that belong to MyClassA. 

So the rule has now become – a variable can reference an object of its declared type or any subtype. 

If you make a mistake and try to use a property that MyClassA doesn't have, then the compiler will tell you about the error at compile time and you are saved a runtime error. 

Why is hierarchical typing useful?

It is useful because it allows you to write partially generic methods. 

For example, suppose you have a class hierarchy Animal and two subclasses Cat and Dog. As long as you only want to use the methods and properties of Animal you can use hierarchical typing to write a method that can accept Animal as its parameter and use it with objects of type Animal, Cat or Dog. 

When you write a method that works with a type – it also works with all of its subtypes. 

Most languages have a single topmost supertype that all other types derive from – usually called Object or something similar. You can use this to write completely generic methods because a variable of type Object can reference anything. However, notice that because of strong typing the only methods that can be used are those that Object has.

Inheritance as the Model

Why do we use inheritance at all, and why is it related to the idea of subtype?

This is a complicated question and one that causes most arguments. We have already looked at these ideas in Chapters 11 and 12 but they also relate to the concept of type.

The original idea of using objects was to model the real world. In the real world things are objects with properties and even methods that allow the object to do something. Introducing objects into programming was to make it more like an exercise in simulation. Indeed the first object-oriented language was Simula, a language for simulation.

The idea is that in the real world objects are related to one another. The problem is that they are related in complex ways.

As we have already stated, in programming the most basic intent of inheritance is to allow code reuse.

Code reuse doesn’t have much to say about any type relationships. It is tempting to take the next step and to say that if objectB inherits from objectA then it is an objectA as well as being an objectB. A square is a square but it is also a rectangle, say.

The Liskov substitution principle is the best known embodiment of this idea. This says that anywhere you can use an instance of a class, you can use an instance of any subclass. As you can use it in place of the base class, it has to be a subtype of the base class. The reasoning is that the subclass has all of the methods of the base class.

This is often true but it isn’t always true.

For example, by our previous reasoning a square is a subclass of a rectangle but you can’t use a square everywhere you can use a rectangle. The reason is that you cannot set the sides of a square to different values. There is a restriction on a rectangle to make a square.

Restrictions and specializations spoil the neat idea that subtypes can be used in place of their supertype. What this means is that the Liskov substitution principle is more a theoretical simplification than a reflection of the world.

This also makes strong typing an arbitrary theoretical decision when it come to rules for how class instances can be used. You can find ways to make subclasses always work as subtypes. For example if you implement a square as a rectangle that still has two sides specified, you can retain all of the rectangle methods and enforce the equality some other way. This is far from natural.

There is also the problem that in the real world objects are related to multiple other objects. A square is a special case of a rectangle and it is an n-sided equilateral shape.

You could try to model this by using Python’s multiple inheritance but this is usually much more difficult than it seems when you first start out on the enterprise. This is the reason most other languages restrict themselves to single inheritance. Other languages add to classes the idea of interfaces – essentially class declarations with no implementation. This allows for a limited form of multiple inheritance but does nothing for code reuse, forcing programmers to return to copy-and-paste code reuse.

The problem is that the real world is often not modeled well by a single inheritance hierarchy, whether used with or without strong typing.

This is the main reason that you will hear advice such as “prefer composition over inheritance”. The idea that one object contains another object is in many ways an easier concept to work with. So, for example, a car object might contain a steering wheel object and four road wheel objects which in turn contain wheel objects. However, this doesn’t always fit, how does a square contain a rectangle object, and current languages provide poor support for composition.

In chapter but not in this extract

  • Python and Type
  • Instance and Subclass
  • Duck Typing

Summary

  • There are two main concepts of type. The first primitive type relates to the way data is represented and it is the origin of the concept. The second relates to class in object oriented programming.

  • In many object oriented languages defining a class introduces a new type to the type system.

  • A strongly typed language enforces the rule that you have to declare the type of a variable and that variable can from then on only reference objects of that type.

  • This rule is usually extended to include variables referencing objects of the specified type or a subtype. The reason for this is that it allow you to write partly generic methods which can process the type and all its subtypes.

  • Once again this leads to the idea that the class hierarchy is a model of the real world and this is usually not the case.

  • The Liskov substitution principle is often used as a justification for hierarchical typing but it is only an approximation to the real world.

  • In Python, variables do not have a type associated with them and can reference any type of object.

  • In Python, objects do have a limited notion of type in that their __class__ attribute is set to the class or metaclass that created them. However, it is important to realize that __class__ can be modified.

  • You can use isinstance and issubclass to check that an object claims to be of the appropriate type.

  • Another approach is to use defensive programming to test for the presence of any attribute or method you are planning to use via the hasattr function. This is generally called duck typing.

Programmer's Python
Everything is an Object
Second Edition

Is now available as a print book: Amazon

pythonObject2e360

Contents

  1. Get Ready For The Python Difference
  2. Variables, Objects and Attributes
  3. The Function Object
  4. Scope, Lifetime and Closure
  5. Advanced Functions
  6. Decorators
  7. Class, Methods and Constructors
      Extract 1: Objects Become Classes ***NEW!
  8. Inside Class
  9. Meeting Metaclasses
  10. Advanced Attributes
  11. Custom Attribute Access
  12. Single Inheritance
  13. Multiple Inheritance
  14. Class and Type
  15. Type Annotation
  16. Operator Overloading
  17. Python In Visual Studio Code

 Extracts from the first edition

Advanced Attributes

Related Articles

Creating The Python UI With Tkinter

Creating The Python UI With Tkinter - The Canvas Widget

The Python Dictionary

Arrays in Python

Advanced Python Arrays - Introducing NumPy

pythondata

 



 

Comments




or email your comment to: comments@i-programmer.info

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

Banner

<ASIN:1871962587>



Last Updated ( Monday, 08 February 2021 )