Search code examples
pythonoopinstance-variables

Should internal class methods return values or just modify instance variables?


I am creating a query builder class that will help in constructing a query for mongodb from URL params. I have never done much object oriented programming, or designed classes for consumption by people other than myself, besides using basic language constructs and using django's built in Models.

So I have this QueryBuilder class

class QueryHelper():
    """
    Help abstract out the problem of querying over vastly
    different dataschemas.
    """

    def __init__(self, collection_name, field_name, params_dict):
        self.query_dict = {}
        self.params_dict = params_dict
        db = connection.get_db()
        self.collection = db[collection_name]

    def _build_query(self):
        # check params dict and build a mongo query
        pass

Now in _build_query I will be checking the params_dict and populating query_dict so as to pass it to mongo's find() function. In doing this I was just wondering if there was an absolute correct approach to as whether _build_query should return a dictionary or whether it should just modify self.query_dict. Since it is an internal method I would assume it is OK to just modify self.query_dict. Is there a right way (pythonic) way of approaching this? Is this just silly and not an important design decision? Any help is appreciated.


Solution

  • Returning a value is preferable as it allows you to keep all the attribute modifying in one place (__init__). Also, this makes it easier to extend the code later; suppose you want to override _build_query in a subclass, then the overriding method can just return a value, without needing to know which attribute to set. Here's an example:

    class QueryHelper(object):
        def __init__(self, param, text):
            self._param = param
            self._query = self._build_query(text)
    
        def _build_query(self, text):
            return text + " and ham!"
    
    class RefinedQueryHelper(QueryHelper):
        def _build_query(self, text):
            # no need to know how the query object is going to be used
            q = super(RefinedQueryHelper, self)._build_query()
            return q.replace("ham", "spam")
    

    vs. the "setter version":

    class QueryHelper(object):
        def __init__(self, param, text):
            self._param = param
            self._build_query(text)
    
        def _build_query(self, text):
            self._query = text + " and ham!"
    
    class RefinedQueryHelper(QueryHelper):
        def _build_query(self, text):
            # what if we want to store the query in __query instead?
            # then we need to modify two classes...
            super(RefinedQueryHelper, self)._build_query()
            self._query = self._query.replace("ham", "spam")
    

    If you do choose to set an attribute, you might want to call the method _set_query for clarity.