user4601931
user4601931

Reputation: 5294

Is this an appropriate use for decorators?

I have a class like this:

class MyClass(object):
    def __init__(self, name):
        self.name = name
        self.df = pd.read_table(name)

and a bunch of methods like this:

def valid_cond1(self):
    # check if cond1 is satisfied with respect to self.df and return
    # a DataFrame of rows not satisfying cond1   

def valid_cond2(self):
    # same deal

etc. In the end, I want to take the output of each of these methods, and return to the client a formatted list of the bad rows. I can do this for valid_cond1 by adjusting the implementation like so:

def valid_cond1(self):
    # err_df = rows of self.df not meeting cond1
    bad_lines = []
    for ix, val in err_df.iterrows():
        bad_lines.append("Error in line %s: %s.  Cond1 not met.." % (ix,val))
    return bad_lines

But I don't want to write the same logic for each of these functions (perhaps there are very many of them).

Is this a place where I could use a decorator? Or is there another way to achieve the desired behavior?

Upvotes: 1

Views: 86

Answers (1)

Scott Colby
Scott Colby

Reputation: 1430

I wouldn't jump to decorators for something like this, but perhaps a more generic method

def check_condition(self, condition):
    # check the condition
    return bad_lines  # etc.

def valid_cond1(self):
    # define condition_1
    return self.check_condition(condition_1)

def valid_cond2(self):
    return self.check_condition(condition_2)

If you can't make the conditions into something simply passable, you could do something like this to avoid repeating the error printing code at least:

@staticmethod
def invalid_condition(err_df):
    # bad lines stuff here

def valid_cond1(self):
    # calculate err_df
    if err_df:
        return self.invalid_condition(err_df)

EDIT: Just for fun, a decorator version. I've been known to (ab)use decorators, so I can understand the desire:

from functools import wraps

def print_error_info(func):
    @wraps(func)
    def wrapped(*args, **kwargs):
        err_df = func(*args, **kwargs)
        bad_lines = []
        for ix, val in err_df.iterrows():
            bad_lines.append("Error in line %s: %s.  Cond1 not met.." % (ix,val))
        return bad_lines
    return wrapped

# use
class MyClass:
    # *snip*

    @print_error_info
    def valid_cond1(self):
        # whatever you need
        return err_df

Upvotes: 2

Related Questions