Unorthodox Python OOP

Hamed Zaghaghi
2 min readJul 14, 2023

--

Generated with “a programmer with a laptop on the clouds upside-down doing unusual programming, sitting on the floor on a carpet and lots of cushions, artstation, pinterest, high resolution, detailed, 8k, nvinkpunk” prompt and inkpunk-v1 model

It all started when I was browsing Rust documentation and noticed that in Rust language struct methods are defined separately from the struct itself. To understand what I mean, see below example extracted from Rust documentation.

#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}

impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}

What a nice feature! I told to myself. Then I started thinking about this in Python.

Can we do the same in Python?

The short answer is Yes, although we need to write a little bit of code to make this style possible in Python. Here is a simple solution to it with the help of decorators and monkey-patching.

def impl(cls):
def wrapper(fn):
setattr(cls, fn.__name__, fn)
return fn
return wrapper

And here is a simple usage. dataclass is used here to resemble Rust struct as there is no struct in Python.

from dataclasses import dataclass

@dataclass
class Rectangle:
width: int
height: int

@impl(Rectangle)
def area(self: Rectangle) -> int:
return self.height * self.width

if __name__ == "__main__":
rect = Rectangle(width=10, height=20)
print(rect.area())

The definition of area method is now outside of the class definition.

Use cases

I think this style offers a few use cases that can be used in our future Python projects.

Separated Implementation Modules

In some cases, implementing a method requires a few other dependencies which may be large and slow to import. Using this style you can have a class definition and multiple implementation files. Developer only import what they want to use.

Feature Toggles

Using this style, we can have feature toggles to choose between different implementations. Take a look at the following example.

def impl(cls: T, condition: bool=True):
def wrapper(fn):
if condition:
setattr(cls, fn.__name__, fn)
return fn
return wrapper

Note that the above condition is executed when a method is being defined, and not when the method is called.

from dataclasses import dataclass
from enum import IntFlag, auto

@dataclass
class Rectangle:
width: int
height: int

class FeatureFlag(IntFlag):
FAST_AREA = auto()

@impl(FeatureFlag)
def read_from_config():
return FeatureFlag.FAST_AREA

The implementation file has two definition for area method, and the selection between these two is controlled by feature flags.

FLAGS = FeatureFlag.read_from_config()

@impl(Rectangle, FeatureFlag.FAST_AREA in FLAGS)
def area(self: Rectangle) -> int:
return self.height * self.width

@impl(Rectangle, FeatureFlag.FAST_AREA not in FLAGS)
def area(self: Rectangle) -> int:
area_value = 0
for _ in range(self.width):
area_value += self.height
return area_value

if __name__ == "__main__":
rect1 = Rectangle(width=10, height=20)
print(rect1.area())

Issues

Doing this kind of hacking in Python breaks type checkers like mypy .

I’d like to play with programming languages, and make programming more fun to work with.

It would be greatly appreciated if you could share your thoughts/ideas in the comments bellow.

--

--