2

I have a class with many of its methods dependent on the likely, but not certain, existence of another specific instance of the class. These methods would be conditioned along the lines of: if other_instance return X else return None. In fact, I wrote several identical if statements as part of the code for each method. (Also note that I cannot use a custom __init__ because of inheritance issues.) Is there a practical or appropriate approach to limiting this redundant coding other than the None-ing out the dependent methods as I show below (on a greatly simplified model)?

class QuarterResult(ParentClass):
    result = None
    prior = None

    def get_prior_quarter(self):
        # code to find prior_quarter
        if prior_quarter:
            self.prior = prior_quarter
            return prior_quarter

        # commence None-ing out unusable methods
        # Note: the actual model has 10+ methods which need prior

        unusable_methods = ('change_in_results', 'quarterly_return')
        for mthd in unusable_methods:
            setattr(self, mthd, None)

    # assume this method is ALWAYS run first
    def calculate_result(self, *args):
        # result = some calculation based on the given args
        _ = self.get_prior_quarter()
        self.result = result

    @property
    def change_in_results(self):
        return self.prior.result - self.result

    @property
    def quarterly_return(self):
        return self.change_in_results / self.prior.results

    @property
    def real_result(self):
        if self.result == 42:
            return "You are right: it's 42"
        else:
            return 'What do I know?'
Cole
  • 2,489
  • 1
  • 29
  • 48

1 Answers1

5

You could use a custom decorator for that

def dependent(meth):
    def depmeth(self, *args, **kwargs):
        if self.other_instance:
            return meth(self, *args, **kwargs)
        else:
            return None
    return depmeth

then your class would become

class ...:
    ...
    def meth1(self, x):
        return "always working"

    @dependent
    def meth2(self, y):
        return "code executed only if other_instance is present"
6502
  • 112,025
  • 15
  • 165
  • 265
  • I certainly thought of this, and probably overlook decorators too much. Do you have a thought about the value of a decorator in this circumstance compared with the more absolutist approach of redefining a method to an attribute who's value is `None`. – Cole Feb 16 '14 at 18:18
  • 1
    Zeroing out the methods is irreversible. Might the attribute (apparently the object `prior`) be defined later in the lifetime of the class? Might your code be extended in the future so that this can happen? If so, you'll have a difficult to fix design problem. And isn't the undefining code repeated in all your dependent functions? I would let the functions stay, and either bite it and check for `prior` or let it raise an error and catch it higher-- you need to handle `None` somehow anyway. – alexis Feb 16 '14 at 22:18
  • Thanks for your thoughtful response. I learned on other thing: `@property` eliminated the ability to use `setattr(self, 'method_name')`. Your code works great. FYI, you can write depmeth with one line `return self.prior_quarter and mthd(self, *args, **kwargs) or None` – Cole Feb 16 '14 at 23:32
  • @cole: please note that in Python one of the many design ideas was that easing code reading is much more important than easing code writing (code is written once, but read many times during the life of a program). If your aim is saving keystrokes and you are ready to sacrifice readability to it then probably PERL (basically a write-only language) is a better choice. Note also that Python now has a ternary operator `x1 if test else x2`: the and/or trick is quite obsolete (and the way you implemented it could be a problem if meth returns a non-None falsy result like 0). – 6502 Feb 17 '14 at 06:50
  • @6502 you make some good points about `test and x or y` rather than `x if test else y` (although I switched away from single line `if/else` statements). – Cole Feb 17 '14 at 14:50
  • I implemented a decorator using parameters to handle a few different sets of dependencies, is reads `@is_dependent(upon='prior_quarter')`. For more info, this stackoverflow answer is good: http://stackoverflow.com/questions/5929107/python-decorators-with-parameters – Cole Feb 17 '14 at 14:53