Есть несколько вещей, которые мы можем сделать, чтобы очистить код.В конце мы избавимся от глобальной переменной.
Сделаем данные непротиворечивыми
Вместо того, чтобы ждать до конца, чтобы получить содержимое каталога, мы можем получить его в самом начале,Таким образом, позже мы сможем обрабатывать строки текста и содержимое каталога точно так же.
import os
import operator
query_list = ["look", "for", "these"]
search_object = "name_of_file_or_directory"
if os.path.isfile(search_object):
with open(search_object, "r") as file:
lines = file.read().split("\n")
elif os.path.isdir(search_object):
lines = os.listdir(search_object)
# At this point, lines is just a list of text - we don't care where it came from.
search_mode = input("Are you looking for objects that (1) exactly match or (2) contain the query? ")
matched = []
def comparison():
if search_mode == "1" and operator.eq(string, query) or search_mode == "2" and operator.contains(string, query):
matched.append(string)
return matched
for query in query_list:
def search_loop(container):
# container is either list of filenames or list of lines
global string
for string in container:
matched = comparison()
return matched
# We were able to remove checks from here
matched = search_loop(lines)
print(matched)
Когда это имеет смысл, выберите правильную операцию как можно раньше
Раньше мы проверяли каждуювремя какой операции сделать (точное совпадение или содержит).Вместо этого мы можем выбрать опцию справа, когда мы получим пользовательский ввод и назначим его одному имени для последующего использования.
import os
import operator
query_list = ["look", "for", "these"]
search_object = "name_of_file_or_directory"
if os.path.isfile(search_object):
with open(search_object, "r") as file:
lines = file.read().split("\n")
elif os.path.isdir(search_object):
lines = os.listdir(search_object)
search_mode = input("Are you looking for objects that (1) exactly match or (2) contain the query? ")
# Because the interface of both functions is the same we can use them directly.
# If they were not the same then we could write an adapter function that would make them have
# the same signatures so they could be used in the same way below.
if search_mode == "1":
search_op = operator.eq
elif search_mode == "2":
search_op = operator.contains
matched = []
for query in query_list:
for string in lines:
if search_op(string, query):
matched.append(string)
print(matched)
Реорганизация кода в функции
Теперь, когда у нас естьЧтобы лучше представить части программы, мы можем разбить ее на функции.Использование функций дает частям программы конкретные имена, а также помогает сузить область действия переменных, которые они используют.Чем меньше переменных используется в любом фрагменте кода, тем легче это будет понять позже.Мы избегаем глобальных переменных, тщательно выбирая наши функции - одним из важных критериев, который мы могли бы использовать, является то, что все в функции является автономным и не нужно ссылаться на что-либо извне.
Мы также будем использовать if __name__ == '__main__'
соглашение, чтобы мы могли включить наш скрипт из другого скрипта, если захотим.
import os
import operator
def get_contents(search_object):
if os.path.isfile(search_object):
with open(search_object, "r") as file:
return file.read().split("\n")
elif os.path.isdir(search_object):
return os.listdir(search_object)
def get_search_function(user_input):
if search_mode == "1":
return operator.eq
elif search_mode == "2":
return operator.contains
def find_matches(match_function, query_list, lines):
matched = []
for query in query_list:
for string in lines:
if match_function(string, query):
matched.append(string)
return matched
if __name__ == "__main__":
search_object = "name_of_file_or_directory"
lines = get_contents(search_object)
search_mode_input = input("Are you looking for objects that (1) exactly match or (2) contain the query? ")
search_function = get_search_function(search_mode_input)
query_list = ["look", "for", "these"]
matches = find_matches(search_function, query_list, lines)
print(matches)
Дальнейшие улучшения
- Обработка ошибок - везде, где что-то может пойти не так, мы можем
raise
соответствующая ошибка (для чего-то, что может быть обработано) или assert
(для чего-то, чего мы не ожидаем и не можем реально восстановить). - Модули типа
argparse
могутупростить ввод аргументов в сценарий, чтобы нам не приходилось жестко кодировать значения переменных. - Вместо того, чтобы принимать «поисковый объект», а затем беспокоиться о том, является ли это файлом или каталогоммы могли бы также прочитать из
sys.stdin
и оставить его пользователю для генерации текста и передачи его в программу, например: cat words.txt | python script.py
для содержимого или ls . | python script.py
для файлаs в каталоге.