14: Class and Type¶
Summary¶
- There are two main concepts of type
Primitive type: relates to the way data is represented and 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 si that it allows you to write partly generic methods which can process the type and all its subtypes
Class hierarchy is a model of the real world
The Liskov Substitution Principle is often used as a justification for hierarchial typing, but it is only an approximation to the real world
Variables do not have a type associated with them and can reference any type of object
- Objects do have a limited notion of type in that their __class__ attribute is set to the class or metaclass that created them
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”
Program¶
"""
Demonstration of Python Type Concepts
This program shows:
- Primitive types vs object types
- Classes as types in object-oriented programming
- Python's dynamic (not strictly strong) typing
- Class hierarchies and subtype relationships
- Liskov Substitution Principle (conceptual example)
- Type checking with isinstance and issubclass
- Duck typing using hasattr
"""
# -------------------------------
# Primitive Types
# -------------------------------
# Primitive types represent basic data
x = 10 # int
y = 3.14 # float
z = "hello" # string
print("Primitive Types:")
print(type(x), type(y), type(z))
print()
# -------------------------------
# Classes Define New Types
# -------------------------------
class Animal:
"""Base class representing a general animal"""
def speak(self):
return "Some sound"
class Dog(Animal):
"""Dog is a subtype of Animal"""
def speak(self):
return "Bark"
class Cat(Animal):
"""Cat is another subtype of Animal"""
def speak(self):
return "Meow"
# -------------------------------
# Dynamic Typing (Python is NOT strongly typed)
# -------------------------------
# Variables do NOT have fixed types in Python
var = Dog() # var refers to a Dog object
print("var is:", type(var))
var = 42 # same variable now refers to an int
print("var is now:", type(var))
print()
# -------------------------------
# Class Hierarchy & Substitution
# -------------------------------
def animal_sound(animal):
"""
Function that works with Animal or any subtype.
Demonstrates Liskov Substitution Principle:
subtypes (Dog, Cat) can replace base type (Animal)
"""
print(animal.speak())
print("Liskov Substitution Principle Demo:")
animal_sound(Dog())
animal_sound(Cat())
print()
# -------------------------------
# Type Checking
# -------------------------------
dog = Dog()
print("Type Checking:")
print(isinstance(dog, Dog)) # True
print(isinstance(dog, Animal)) # True (subtype relationship)
print(issubclass(Dog, Animal)) # True
print()
# -------------------------------
# Object Type via __class__
# -------------------------------
print("Object __class__ attribute:")
print(dog.__class__)
# NOTE: __class__ can technically be modified (not recommended)
dog.__class__ = Cat
print("After modifying __class__:", dog.__class__)
print(dog.speak()) # Now behaves like a Cat
print()
# -------------------------------
# Duck Typing (hasattr)
# -------------------------------
class Robot:
"""Not related to Animal, but has a speak method"""
def speak(self):
return "Beep boop"
def make_it_speak(obj):
"""
Duck typing: we don't care about the type,
only that the object has a 'speak' method.
"""
if hasattr(obj, "speak"):
print(obj.speak())
else:
print("Object cannot speak")
print("Duck Typing Demo:")
make_it_speak(Dog())
make_it_speak(Robot()) # Works even though Robot is not an Animal
make_it_speak(123) # Fails safely
Program Output¶
Primitive Types:
<class 'int'> <class 'float'> <class 'str'>
var is: <class '__main__.Dog'>
var is now: <class 'int'>
Liskov Substitution Principle Demo:
Bark
Meow
Type Checking:
True
True
True
Object __class__ attribute:
<class '__main__.Dog'>
After modifying __class__: <class '__main__.Cat'>
Meow
Duck Typing Demo:
Bark
Beep boop
Object cannot speak