class Polo():
def __init__(self, color, size, price=99.00, style=None):
self.color = color
self.size = size
self.price = price
self.style = style
def fold(self):
print("FOLDING THE " + self.color.upper() + " POLO!")
def transfer_to(self, store_name):
print(f"SHIPPING THE {self.color.upper()} POLO TO STORE: '{store_name.upper()}'")
Custom Classes
A Class is a representation of one or more objects which share the same or similar properties. Each class is like its own custom data type with attributes, methods, and properties defined by the developer.
Definition
In Python, we define a class using the class
keyword, followed by the name of the class in titlecase. The class definition requires a specific function called __init__
to initialize, or create a new member of the object class. A class definition may contain many other methods and properties as well.
Here is a simple example of a custom class definition:
Here we see some example instance methods called fold
and transfer_to
. All instance methods within a class must take self
as their first required parameter. This is a reference to the instance itself.
Initialization
After defining an object class, we can create any number of new members, or “instances”, of that object class.
Although multiple instances can have different values, the attributes and behaviors are shared across all instances.
Here are some initialization and usage examples for the Polo
class:
= Polo(color="Blue", size="Large", price=4.99)
polo_1 print(type(polo_1))
= Polo(color="Yellow", size="Small")
polo_2 print(type(polo_2))
= Polo(color="Red", size="Large", price=65.00, style="Slim")
polo_3 print(type(polo_3))
<class '__main__.Polo'>
<class '__main__.Polo'>
<class '__main__.Polo'>
Attributes:
print(polo_1.color, polo_1.price)
print(polo_2.color, polo_2.price)
print(polo_3.color, polo_3.price)
Blue 4.99
Yellow 99.0
Red 65.0
Methods:
polo_1.fold()
polo_2.fold() polo_3.fold()
FOLDING THE BLUE POLO!
FOLDING THE YELLOW POLO!
FOLDING THE RED POLO!
"Washington, DC")
polo_1.transfer_to("New York, NY")
polo_2.transfer_to("Boston, MA") polo_3.transfer_to(
SHIPPING THE BLUE POLO TO STORE: 'WASHINGTON, DC'
SHIPPING THE YELLOW POLO TO STORE: 'NEW YORK, NY'
SHIPPING THE RED POLO TO STORE: 'BOSTON, MA'
Decorators and Special Methods
We can use a handful of “special methods” and method decorators to supercharge our classes. Special methods are sometimes known as “dunder methods” because they start and end with double underscores. Decorators are defined with preceding @
character.
The class definition below shows an example of using the __repr__
special method, or “representation” function, which determines how the instance should be displayed when printed. It also implements the __iter__
method, which determines what is returned when the item is converted to a dictionary.
Common decorators include:
@property
: when you want to invoke the method as a noun, without trailing parentheses@classmethod
: when you want to invoke the method on the class itself, instead of on an instance of that class@staticmethod
: when the method doesn’t need any information about the instance (i.e. no references toself
)
This class uses the @property
decorator, which allows us to invoke a given method without trailing parentheses (e.g. team.full_name
instead of team.full_name()
). This is basically a stylistic choice on our part, enabling us to use properties to represent nouns, and methods to represent verbs.
class Team():
def __init__(self, city, name):
self.city = city
self.name = name
def __repr__(self):
return f"<Team '{self.name}'>"
def __iter__(self):
yield "city", self.city
yield "name", self.name
@property
def full_name(self):
return self.city + " " + self.name
def advertise(self):
print("COME TO", self.city.upper(), "TO SEE OUR GAMES!")
Here are some initialization and usage examples for the Team
class:
= Team(city="Seattle", name="Storm")
team1 = Team(city="Connecticut", name="Sun")
team2
print(type(team1))
print(type(team2))
<class '__main__.Team'>
<class '__main__.Team'>
Special methods:
print(team1)
print(dict(team1))
print(team2)
print(dict(team2))
<Team 'Storm'>
{'city': 'Seattle', 'name': 'Storm'}
<Team 'Sun'>
{'city': 'Connecticut', 'name': 'Sun'}
Properties:
print(team1.full_name)
print(team2.full_name)
Seattle Storm
Connecticut Sun
Normal methods:
team1.advertise() team2.advertise()
COME TO SEATTLE TO SEE OUR GAMES!
COME TO CONNECTICUT TO SEE OUR GAMES!
Class Inheritance
Class inheritance is an important feature in object-oriented programming (OOP) that allows a new class (known as the “child” or “subclass”) to inherit attributes and methods from an existing class (known as the “parent” or “superclass”). This provides several important benefits:
Code Reusability: Inheritance allows you to reuse code from an existing class, reducing duplication. The child class can inherit all functionality from the parent class and modify or extend it without rewriting everything from scratch.
Extensibility: Inheritance makes it easy to extend the functionality of an existing class. You can add new features or override methods in the child class to change or enhance behavior.
Maintainability: Centralized logic in a parent class means that changes to the core functionality only need to be made in one place. This simplifies maintenance and reduces the chances of errors when updating code.
Polymorphism: Inheritance supports polymorphism, where child classes can be used interchangeably with their parent classes. This is useful in scenarios where you want different behaviors for different subclasses while maintaining a common interface.
In this example, we are creating a BaseballTeam
child class that inherits from the Team
parent class, while adding in some baseball-specific functionality.
class BaseballTeam(Team):
def __init__(self, city, name, short_stop=None, closing_pitcher=None):
# ATTRIBUTES SHARED WITH PARENT:
super().__init__(city=city, name=name)
# CHILD-SPECIFIC ATTRIBUTES:
self.short_stop = short_stop
self.closing_pitcher = closing_pitcher
# OVERRIDING PARENT METHOD:
def advertise(self):
print("COME TO", self.city.upper(), "TO SEE OUR BASEBALL GAMES, INCLUDING:")
print("...", self.short_stop)
print("...", self.closing_pitcher)
# CHILD-SPECIFIC METHOD:
def warm_up_bullpen(self):
print("NOW WARMING UP:", self.closing_pitcher)
In this child class, in the initialization method, we invoke super()
, which is a reference to the parent class, to initialize an instance of the parent class. Then we initialize some baseball specific parameters (in this case short_stop
and closing_pitcher
).
Here are some initialization and usage examples for the BaseballTeam
child class:
= BaseballTeam(name="Yankees", city="New York",
bt ="Derek Jeter",
short_stop="Mariano Rivera"
closing_pitcher
)print(type(bt))
print(bt)
<class '__main__.BaseballTeam'>
<Team 'Yankees'>
Inherited from the parent class:
print(bt.city)
print(bt.name)
print(bt.full_name)
New York
Yankees
New York Yankees
Overridden / customized in the child class:
bt.advertise()
COME TO NEW YORK TO SEE OUR BASEBALL GAMES, INCLUDING:
... Derek Jeter
... Mariano Rivera
Child-specific:
bt.warm_up_bullpen()
NOW WARMING UP: Mariano Rivera
Multiple Inheritance
It is possible to use inheritance to “mix-in” functionality from multiple parent classes:
class Runner:
def run(self):
return "Runs on the track."
class Swimmer:
def swim(self):
return "Swims in the pool."
class Bicyclist:
def bike(self):
return "Cycles on the road."
# INHERITS FROM / "MIXES IN" MULTIPLE PARENTS:
class Triathlete(Runner, Swimmer, Bicyclist):
pass
= Triathlete()
triathlete
print(triathlete.run())
print(triathlete.swim())
print(triathlete.bike())
Runs on the track.
Swims in the pool.
Cycles on the road.
Additional Resources
Reference:
- https://docs.python.org/3/tutorial/classes.html
- https://docs.python.org/3/tutorial/classes.html#class-objects
- https://www.w3schools.com/python/python_classes.asp
- https://www.tutorialspoint.com/python/python_classes_objects.htm
- https://realpython.com/python3-object-oriented-programming
- https://realpython.com/instance-class-and-static-methods-demystified