I have a series of functions that serve to classify data. Each function is passed the same input. The goal of this system is to be able to drop in new classification functions at will without having to adjust anything.
To do this, I make use of a classes_in_module
function lifted from here. Then, every classifier in one python file will be ran on each input.
However, I am finding that implementing the classifier as either a class or a function is kludgy. Classes mean instantiating and executing, while functions lack clean introspection to allow me to query the name or use inheritance to define common values.
Here is an example. First, the class implementation:
class AbstractClassifier(object):
@property
def name(self):
return self.__class__.__name__
class ClassifierA(AbstractClassifier):
def __init__(self, data):
self.data = data
def run(self):
return 1
This can then be used in this fashion, assuming that classifier_list
is the output of classes_in_module
on a file containing ClassifierA
among others:
result = []
for classifier in classifier_list:
c = classifier(data)
result.append(c.run())
However, this seems a bit silly. This class is obviously static, and doesn't really need to maintain its own state, as it is used once and discarded. The classifier is really a function, but then I lose the ability to have a shared name
property -- I would have to use the ugly introspection technique sys._getframe().f_code.co_name
and replicate that code for each classifier function. And any other shared properties between classifiers would also be lost.
What do you think? Should I just accept this mis-use of classes? Or is there a better way?
Functions can have member data. You can also find the name of a function using the func_name
attribute.
def classifier(data):
return 1
classifier.name = classifier.func_name
print(classifier.name) #classifier
If you want multiple functions to behave the same way, you can use a decorator.
function_tracker = []
def add_attributes(function):
function.name = function.func_name
function.id = len(function_tracker)
function_tracker.append(function)
return function
@add_attributes
def classifier(data):
return 1
print(classifier.name, classifier.id) # 'classifier', 0
Would this work to avoid classes in your specific case?
If you don't need several instances of the class (and it seems you don't) make one instance of the class and change the run
to __call__
:
class AbstractClassifier(object):
@property
def name(self):
return self.__class__.__name__
class ClassifierA(AbstractClassifier):
def __call__(self, data):
return 1
ClassifierA = ClassifierA() # see below for alternatives
and then in your other code:
result = []
for classifier in classifier_list:
result.append(classifier(data))
Instead of having ClassifierA = ClassifierA()
(which isn't very elegant), one could do:
classifier_list = [c() for c in (ClassifierA, ClassifierB, ...)]
This method allows you to keep your classes handy should you need to create more instances of them; if you don't ever need to have more than one instance you could use a decorator to IAYG (instantiate as you go ;) :
def instantiate(cls):
return cls()
@instantiate
class ClassifierZ(object):
def __call__(self, data):
return some_classification
To use a class instance as a function:
class ClassifierA(AbstractClassifier):
def __init__(self, data):
self.data = data
def __call__(self):
return 1
result = []
for classifier in classifier_list:
c = classifier(data)
result.append(c())
Or to just use functions:
classifier_list = []
def my_decorator(func):
classifier_list.append(func)
return func
@my_decorator
def classifier_a(data):
return 1
result = []
for classifier in classifier_list:
c = classifier(data)
result.append(c)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With