#python #refactoring
Вопрос:
Нужен совет о том, как провести рефакторинг такого рода кода, как вы можете видеть, базовый код для всех, слева, справа один и тот же, все, что меняется, — это .strip (),. lstrip (),. rstrip (), можно ли провести рефакторинг «красивым» способом?
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
Комментарии:
1. возможно, использование словарей, также
input
действительно не следует использовать в качестве названия для чего-либо, это встроенный2. Согласитесь с использованием приведенных выше словарей. Вашими вариантами значений словаря будут либо сами методы (например
entry.strip
), либо их имена (например'strip'
), которые затем можно будет просмотреть с помощьюgetattr
.3. Используйте «getattr», см. мой ответ, пожалуйста
4. ну, оба приведенных ниже ответа сами по себе «красивы», трудно решить, какой стиль (дикт или лямбда), есть ли какая-либо разница в том, «делать и не делать»
5. в опции лямбда вы все равно повторяете 3 раза, используя «getattr», вам не нужно повторять снова и снова
Ответ №1:
Для этого вы можете использовать диктант:
modes_to_func = {'all': str.strip, 'left': str.lstrip, 'right': str.rstrip}
Таким образом, вы можете избежать повторения 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
Ответ №2:
Так что, очевидно, вы можете сделать что-то подобное str.strip(string_to_strip)
(вроде как имеет смысл, потому что вы просто передаете экземпляр ( self
)), что позволяет это сделать (с несколькими тестовыми примерами в конце).:
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')
Примечание: по какой-то причине PyCharm это не нравится из-за какого-то неожиданного аргумента, хотя прямое выполнение этого не вызывает такого предупреждения
Ответ №3:
Я бы использовал словарь и переместил весь избыточный код из подфункции в основную функцию.
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()]))
Комментарии:
1. в
def leftSelected(inp): return x.lstrip()
inp есть опечатка, она должна быть x, верно?2. Обновлено, спасибо за улов
Ответ №4:
Вы можете переместить дубликат кода в функцию и настроить его с помощью лямбда:
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)
Ответ №5:
Итак, просмотрев все ответы, которые я придумал, это более или менее объединение 3 ответов с диктами, спасибо всем вам за ваши подсказки (например, ввод)
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
Комментарии:
1. вы должны отменить
return
привязку , в противном случае это будетreturn result
только в том случае, еслиdata
это не строка, и в этомresult = None
нет необходимости, вы всегда будете получать что-то в результате, в качестве альтернативы вы можете сделать что-то подобноеreturn result or None
, если вы хотите явно вернутьNone
, напримерresult
, если это пустой список2. да, верно, спасибо, я думаю, что это было копирование и вставка ^^