classDiagram
class my_ns
my_ns : my_data_1
my_ns : my_data_2
my_ns : my_data_3
my_ns : some_function()
The Class on Classes
What is object oriented programming?
Warning
Object Oriented is not synonymous with “better”
Start with a dictionary
Now switch it for a simple name space
Why not attach a function too?
classDiagram
class my_ns
my_ns : my_data_1
my_ns : my_data_2
my_ns : my_data_3
my_ns : some_function()
classDiagram
class Class
Class : class_data = 12
class instance
instance : instance_data=42
instance --> Class
classDiagram
class Class1
Class1 : class_1_data = 12
class instance
instance : instance_data=42
class Class2
Class2 : class_2_data = 13
instance --> Class1
Class1 --> Class2
classDiagram
class Class1
Class1 : class_1_data = 12
class instance_1
instance_1 : instance_data=42
class instance_2
instance_2 : instance_data= 30
class Class2
Class2 : class_2_data = 13
instance_1 --> Class1
instance_2 --> Class1
Class1 --> Class2
classDiagram
class Class1
Class1 : data = 42
class instance
instance : data = 42
class Class2
Class2 : data = 13
instance --> Class1
Class1 --> Class2
classDiagram
class Class1
Class1 : class_data = 10
Class1 : some_func(param1, param2)
class instance
instance : data = 42
instance --> Class1
Instance methods created from class functions
classDiagram
class Class1
Class1 : class_data = 10
Class1 : some_func(param1, param2)
class instance
instance : data = 42
instance --> Class1
Dog())from pathlib import Path
class Dog:
def __init__(self, name):
self.name = name
@classmethod
def from_file(cls, path):
with Path(path).open('r') as fi:
name = fi.read()
return cls(name=name)class Dog:
def __init__(self, name):
self.name = name
@staticmethod
def add(arg1, arg2):
return arg1 + arg2Dunders
Names with double leading and trailing underscores. Many are part of the python language.
pyfile.py
Python has an object creation step:
__new__
Which creates an empty object (sets up space for data)
and initialization step:
__init__
Which fills in data
Usually, avoid __new__
get_/set_.get_/set_ methods
getter and setter
Inheritance is used to extend or modify classes
super lets you access the immediate parent class’ namespace
Python supports multiple inheritance, which can get confusing
MRO (__mro__) defines lookups (instance) -> (class) -> (parent) -> (grandparent) -> …
super is used to skip the current class in the mro.
You can check the MRO via the __mro__ dunder class attribute.
(<class 'pandas.core.frame.DataFrame'>, <class 'pandas.core.generic.NDFrame'>, <class 'pandas.core.base.PandasObject'>, <class 'pandas.core.accessor.DirNamesMixin'>, <class 'pandas.core.indexing.IndexingMixin'>, <class 'pandas.core.arraylike.OpsMixin'>, <class 'object'>)
Checking class relations is done with isinstance and issubclass
the dataclasses module makes class definitions more ergonomic.
It can:
__init__, __equal__, order dunders, __hash__The dataclasses module (of the standard library) was inspired by attrs. It also serves similar purposes as pydantic but does not perform any runtime type checking. If you want typehints to be enforced, use pydantic.
Traditional class definition
Equivalent dataclass
Protocols (sometimes called interfaces):
Common Protocols (most found in types or typing module):
- Sequence
- Mapping
- Collection
- Hashable
- Reversible
- Sized
- Iterable
- ...
A class which implements the Sized protocol
A class which implements the Hashable protocol
Defining protocols
abc module and typing.Protocolisinstance checks required.This is a great article on the difference between ABC and typing.Protocol. See ABC module and typing.Protocol docs for more details.
ABC example
ABC use
from random import choice, sample
class MyDeck(DeckABC):
"""A deck with cards 1-10"""
def __init__(self, cards=None):
if cards is None:
cards = list(range(1,11))
self.cards = cards
def shuffle(self):
cards = sample(self.cards, len(self.cards))
return MyDeck(cards)
def draw(self):
card = choice(self.cards)
self.cards.pop(card)
return card
mydeck = MyDeck()typing.Protocol example
typing.Protocol use
from typing import Self
from random import choice, sample
class MyDeck:
"""A deck with cards 1-10"""
def __init__(self, cards=None):
if cards is None:
cards = list(range(1,11))
self.cards = cards
def shuffle(self) -> Self:
cards = sample(self.cards, len(self.cards))
return MyDeck(cards)
def draw(self) -> str:
card = choice(self.cards)
self.cards.pop(card)
return card
deck = MyDeck()
assert isinstance(deck, DeckProtocol)Possible Advantages
Possible Disadvantages
Others?
A few well-loved python classes:
before pathlib
With pathlib
A few dreaded python OO libraries:
Most are wrappers around languages with less flexible type systems or overly dynamic.