Lecker Spam
Lecker Spam

Reputation: 31

Refactoring Python Code when the only thing changes are methods?

Need some advice on how to refactoring this kind of code, as you can see, the basic code for all, left, right is the same, all what is changing is .strip(), .lstrip(), .rstrip(), is it possible to refactor this in a "beautiful" manner?

def clean_whitespaces(input, mode='all', ):
    result = None
    modes = (None, 'all', 'left', 'right')
    if mode not in modes:
        raise ValueError('clean_whitespaces: mode must be one of {}.'.format(modes))

    if mode == None:
        mode = 'all'

    if mode == 'all':
        if isinstance(input, str):
            result = input.strip()
        else:
            input = list(input)
            result = [entry.strip() for entry in input]
        return result

    elif mode == 'left':
        if isinstance(input, str):
            result = input.lstrip()
        else:
            input = list(input)
            result = [entry.lstrip() for entry in input]
        return result

    elif mode == 'right':
        if isinstance(input, str):
            result = input.rstrip()
        else:
            input = list(input)
            result = [entry.rstrip() for entry in input]
        return result

Upvotes: 2

Views: 183

Answers (5)

Bar Gans
Bar Gans

Reputation: 1663

You can use a dict for this:

modes_to_func = {'all': str.strip, 'left': str.lstrip, 'right': str.rstrip}

This way, you can avoid iterate over the modes:

def clean_whitespaces(input, mode='all', ):
    modes = (None, 'all', 'left', 'right')
    modes_to_func = {'all': str.strip, 'left': str.lstrip, 'right': str.rstrip}
    if mode not in modes:
        raise ValueError('clean_whitespaces: mode must be one of {}.'.format(modes))

    if mode is None:
        mode = 'all'

    if isinstance(input, str):
        result = modes_to_func[mode](input)

    else:
        input = list(input)
        result = [modes_to_func[mode](entry) for entry in input]
    return result

Upvotes: 4

itprorh66
itprorh66

Reputation: 3288

I would use a dictionary and move all redundant code out of sub function and into main function.

def clean_whitespace(inp, mode='all'):

    def allSelected(x):
        return x.strip()

    def rightSelected(x):
        return x.rstrip()        
   
    def leftSelected(x):
        return x.lstrip()

    mdict = {'all': allSelected , 'left': leftSelected, 'right': rightSelected} 
    if mode == None:
        mode = 'all'
    try:
        if isinstance(inp, str):
            return mdict[mode](inp)
        else:
            return [mdict[mode](entry) for entry in inp]
    except KeyError:
        print('clean_whitespaces: mode must be one of {}.'.format([mdict.keys()]))

Upvotes: 1

Matiiss
Matiiss

Reputation: 6156

So apparently you can do sth like str.strip(string_to_strip) (kinda makes sense because you simply pass in the instance (self)) which allows for this (with a few test cases at the end):

def clean_whitespaces(data, mode='all'):
    modes = {None: str.strip, 'all': str.strip,
             'left': str.lstrip, 'right': str.rstrip}
    if mode not in modes:
        raise ValueError(f'clean_whitespaces: mode must be one of '
                         f'{", ".join(map(repr, modes.keys()))}.')

    if isinstance(data, str):
        result = modes[mode](data)
    else:
        result = [modes[mode](str(entry)) for entry in list(data)]
    return result


case = '                      super long spaces before                           '
correct_cases = [
    'super long spaces before',
    'super long spaces before',
    'super long spaces before                           ',
    '                      super long spaces before'

]
for m, c in zip((None, 'all', 'left', 'right'), correct_cases):
    assert clean_whitespaces(case, 'w') == c
print('passed assertion')

Note: for some reason PyCharm doesn't like this because of some unexpected argument although doing it directly doesn't raise that warning

Upvotes: 1

Lecker Spam
Lecker Spam

Reputation: 31

So after reviewing all of the answers i came up with these, its more or less a merge of the 3 answers with dicts, thanks all of you for your hints (like input)

def clean_whitespaces(data, mode='all', ):
result = None
modes = {
    'all': str.strip,
    'left': str.lstrip,
    'right': str.rstrip
    }

if mode not in modes:
    raise ValueError('clean_whitespaces: mode must be one of {}.'.format([key for key in modes.keys()]))

if isinstance(data, str):
    result = modes[mode](data)
else:
    data = list(data)
    result = [modes[mode](entry) for entry in data]
    return result

Upvotes: 0

alex_noname
alex_noname

Reputation: 32153

You can move the duplicate code into a function and customize it using a lambda:

def strip(strip_foo, input):
    if isinstance(input, str):
        result = strip_foo(input)
    else:
        input = list(input)
        result = [strip_foo(entry) for entry in input]
    return result


def clean_whitespaces(input, mode='all', ):
    result = None
    modes = (None, 'all', 'left', 'right')
    if mode not in modes:
        raise ValueError('clean_whitespaces: mode must be one of {}.'.format(modes))

    if mode == None:
        mode = 'all'

    if mode == 'all':
        return strip(lambda v: v.strip(), input)
    elif mode == 'left':
        return strip(lambda v: v.lstrip(), input)
    elif mode == 'right':
        return strip(lambda v: v.rstrip(), input)

Upvotes: 0

Related Questions