Classes
Blueprints for creating objects. They define attributes (variables) and methods (functions) that objects instantiated from the class will have. self refers to the object instance
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
self._protected = True
self.__private = True
def greet(self):
return f"Hi, I'm {self.name}"
person = Person("Alice", 25)
print(person.greet())
Methods
Different types of methods include: instance methods, class methods, and static methods
class Person:
def __init__(self, name):
self.name = name
# instance method belongs to the instance
def greet(self): # self refers to the instance
return f"Hi, I'm {self.name}"
@classmethod # belongs to the class not the instance
def info(cls): # cls not self
cls.species = "homo sapien"
print(f"I am a {cls.species}")
@staticmethod # helper method that belongs to the class, but doesnt use attributes
def name_generator(letter): # doesnt take on cls or self
if letter == "a":
print("Arthur")
else:
print("Sam")
person = Person('Bob')
person.greet() # Hi, I'm Bob
Person.info() # I am a homo sapien
Person.name_generator('a') # Arthur
Abstract Classes
Blueprints for other classes, cannot be instantiated directly, must be inherited. Abstract methods must be overridden by whatever inherits the abstract class.
from abc import ABC, abstractmethod
class Animal(ABC): # cant be instantiated, must be inherited
def __init__(self, name, age):
self.name = name
self.age = age
@abstractmethod # must be overridden by subclass
def sleep(self):
pass
Inheritance
Allows a class to inherit from another. super initializes the parent class.
class Animal: # base class
def __init__(self, species):
self.species = species
class Dog(Animal): # subclass
def __init__(self, name):
super().__init__("Dog") # passes init args to base class
self.name = name
dog = Dog("Buddy")
print(dog.species, dog.name) # Dog Buddy
Overriding/Polymorphism
When the subclass overrides a base/parent class method.
class Animal:
def speak(self): # parent class method
return "Some sound"
class Dog(Animal):
def speak(self): # overrides the parent class method
return "Bark"
animals = [Dog(), Animal()]
for animal in animals:
print(animal.speak()) # Bark, Some sound
Multiple Inheritance
A class can inherit from multiple classes
class Mammal: # base class
def __init__(self, color):
self.color = color
class WingedAnimal: # mixin class (implements a single, well defined feature)
def __init__(self):
print('I have wings')
class Bat(Mammal, WingedAnimal): # subclass with multiple inheritance
def __init__(self):
Mammal.__init__(self, 'brown') # call each class init method
WingedAnimal.__init__(self)
print('I\'m a bat')
bat = Bat() # I have wings, I'm a bat
help(Bat) # shows the MRO (Method Resolution Order) Bat -> Mammal -> WingedAnimal -> object
Decorators
A function that wraps and extends another function without modifying it (syntactic sugar)
def my_decorator(func):
def wrapper(*args, **kwargs): # *args, **kwargs so it can take in any args
print("1 Hi.")
func()
print("3 Hey.")
return wrapper
@my_decorator # decorates the say_hello function
def say_hello():
print("2 Hello!")
say_hello() # 1 Hi. 2 Hello! 3 Hey.
Closures
A nested function that accesses variables from its enclosing scope. The inner function remembers variables even after the outer function has finished execution
def outer_function(msg):
def inner_function(): # a closure
print(msg) # references msg from the outer function's scope
return inner_function
hi_func = outer_function('hi')
hi_func() # hi
Manipulating Built-ins
Magic Methods (Dunder Methods)
Built in methods that are not usually meant to be called by us but we can override some of them to change their behavior
class Animal:
animal_count = 0
def __init__(self, name): # creates class instance
self._name = name
Animal.animal_count += 1
def __str__(self): # string representation (shown on print)
return f"Hi I'm {self._name})"
def __repr__(self): # representation (how to use the class)
return f"Animal({self._name})"
animal = Animal("Buddy") # new instance animal
print(animal) # __str__ Hi I'm Buddy
print(repr(animal)) # __repr__ Animal(Buddy)
Inheriting from Built-in Classes
Built-in classes like list, dict, and tuple can be extended/inherited from.
class IntFloatList(list): # inherits list methods
def append(self, inpuut):
if isinstance(inpuut, (int, float)):
super().append(inpuut) # calls method from super() directly
else:
print("Only integers or floats are allowed")
my_list = IntFloatList()
my_list.append(3.14)
my_list.append("text") # Only integers or floats are allowed
print(my_list) # [3.14]