Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I'm trying to figure out a good practice for designing a function with (many) optional components.

For a specific example, say I am interested in designing a feature extractor function that takes as input a document and returns a list of features extracted from the document.

Question

If there are many optional components, what kind of approach would be considered good practice and scalable?

Below are a couple options I have been able to think of, though there may be other approaches that I have not considered.

Approach 1: class based

class FeatureExtractor(object):
    """Extract features from text for use in classification."""
    def __init__(self, term_frequency=False, consider_negation=False,
                 pos_tags=False):
        self.term_frequency = term_frequency
        self.consider_negation = consider_negation
        self.pos_tags = pos_tags
        # Could be many more ...

    def extract(self, document):
        """Extract features from a document."""
        features = []
        if self.term_frequency:
            features.extend(self.extract_term_frequency(document))
        if self.consider_negation:
            features.extend(self.extract_negation(document))
        if self.pos_tags:
            features.extend(self.extract_pos_tags(document))
        return features

    def extract_term_frequency(self, document):
        pass

    def extract_negation(self, document):
        pass

    def extract_pos_tags(self, document):
        pass

extractor = FeatureExtractor(term_frequency=True, consider_negation=True,
                             pos_tags=True)
extractor.extract(document)

Approach 2: function arguments

def extract(document, *functions):
    """Extract features from a document."""
    features = []
    for function in functions:
            features.extend(function(document))
    return features

def extract_term_frequency(document):
    pass

def extract_negation(document):
    pass

def extract_pos_tags(document):
    pass

extract(document, extract_term_frequency, extract_negation, extract_pos_tags)

Approach 3: class with mixins or multiple inheritance

Something of a combination of the first and second approach, though I'm not sure how this would be done.

Any ideas on a direction to head would be greatly appreciated!

share|improve this question
    
Am I the only one around here thinking about factories? –  Florian Margaine Mar 4 '13 at 17:35
add comment

2 Answers

up vote 1 down vote accepted

I think the central question you should be asking yourself here is "what sort of features are you expecting to extract from a document"?

If you want to be able to add new feature extractor functions with ease, go for the second version, where each feature gets extracted independently and handles the document by itself.

On the other hand, if you know upfront what sort of features you are going to need, using a central extractor that takes advantage of that knowledge to extract things all at once (using parameters to tune the features, ofc)

As for classes vs functions I honestly don't think they are too different one from the other, since in the end they encapsulate things the same way. In Python's case I would recommend using "callables" when possible (so you can use either functions or objects defining __call__ where appropriate) and I would always use functions over classes unless I need to keep track of some extra internal state (Python closures suck due to the local by default scoping)

share|improve this answer
    
This raises some really good points (things to consider), especially about using a centralized approach if I'm already pretty sure upfront about the sort of features that will be needed. Also, thanks for pointing out "callables", which I had not heard of before but sound potentially promising. –  Wesley Baugh Mar 5 '13 at 9:18
add comment

With what you are showing there I prefer the function arguments because there is less code involved to go wrong. I would still use some kind of class to join them together.

However, if I were implementing a system like this I would be much less tied to the implementation. I'm not sure what you mean by "features extracted from a document" but let's imagine that the document was the specification for a car and mentions that it has alloy wheels, runs on petrol or diesel and so on. If I wanted to extract that data, depending on whether I wanted to approach things from a feature-first or document-first direction I would be thinking of creating a collection of the features I was interested in or a collection of the features that I could extract from the document.

In the first case I might say:

def featuresILike = { "alloy wheels":"/alloy wheels/", "v8 engine":"/V8/" } and so on.

Then I can just run through the document looking for matches to those patterns and adding the related key to this documents feature metadata if I find it.

In the second case I might look for the pattern indicating a list of features, and then try to extract all the features listed in a particular document into the metadata. That way I can just query against all of them as necessary.

The conceptual difference with this type of approach is that I am aiming to treat data as data and then use my code to extract it. Every time I write a "hasAlloyWheels()" type function what I'm really doing is making my data into code. Sometimes you might need to do that, but if you can extract that information into configuration or narrow down your code so it only happens in a couple of places, you're going to end up with something that is both more reusable and easier to maintain.

share|improve this answer
    
This is a great write up, and is definitely a good approach for the kind of situation you describe. However, in this case I was planning on extracting (literally) every word as a "feature", which is a term that I should have described in the question as referencing machine learning problems. Either way, +1 because this is still a good approach for certain kinds of problems. –  Wesley Baugh Mar 5 '13 at 9:11
    
I see, not terminology I was aware of. In that case I think you would want a more detailed data structure- if you are looking at classifying your data potentially on multiple axes then conceptually you are probably going to need a tree or network of some kind, I would imagine that approaches to this are well defined within the field. I would still be taking the core approach of separating the data you are operating on from the operations you are performing as far as possible. If that is not possible, consider a language that does not distinguish between data and code. –  glenatron Mar 5 '13 at 10:47
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.