A 'Class Only Design' is an attempt to merge some aspects of Object Oriented and Functional programming. Specifically, it:
- allows you to group like functions and attributes in a common namespace,
- lets you use inheritance to override certain functions, e.g., to implement the template pattern,
- embraces the functional concept of immutability over changing state.
A class only class is, in other words, an approach for creating an immutable singleton object.
pip install class_only_design
The class_only
decorator enforces class only design:
from class_only_design import ClassOnly
class VeryFunctional(ClassOnly):
attribute = 5
@classmethod
def method(cls):
return a_big_computation(cls.attribute)
It makes the class uninstantiable:
instance = VeryFunctional()
Traceback (most recent call last):
...
TypeError: Class Only classes cannot be instantiated
And also immutable:
VeryFunctional.attribute = 6
Traceback (most recent call last):
...
TypeError: Class Only classes are immutable
It'll also prevent you from adding methods that are used to instantiate classes:
class AwesomeClass(ClassOnly):
def __init__(self):
"Create an awesome instance!"
Traceback (most recent call last):
...
TypeError: ('Class Only classes cannot define __init__', <class '__main__.AwesomeClass'>)
Conceptually, a class_only class can have only one state. However, creating that initial state may be expensive, and you may not want to incur that expense at class creation time. For example, consider the following example:
class Methodology(ClassOnly):
config = read_large_config_file()
@classmethod
def process(cls):
for setting in cls.config:
# do work
The time cost of calling read_large_config_file
is incurred when Methodology
is created, which for most classes means module load time. To address this issue, class_only provides the constant
decorator. Decorating a method with @constant
creates a class property that is called at most once. Using constant
, the above example becomes:
from class_only_design import constant
class Methodology(ClassOnly):
@constant
def config(cls)
return read_large_config_file()
@classmethod
def process(cls):
for setting in cls.config:
# do work
In the second example, read_large_config_file
isn't called until Methodology.process
is. Note that @constant
ensures that the config
method is only ever executed once, even if Methodology.process
is called multiple times.