Как избежать глобальной переменной и повторения в python - PullRequest
0 голосов
/ 20 января 2019

Я написал скрипт, который просматривает список строк и ищет совпадения либо (1) строк в файле, либо (2) имен файлов в каталоге.

В зависимости от режима поиска (пользовательский ввод) каждая строка добавляется в список, если (1) она точно соответствует строке / имени файла или (2) содержит строку / имя файла.

import os
import operator

query_list = ["look", "for", "these"]

search_object = "name_of_file_or_directory"

if os.path.isfile(search_object):
    input_object = 1
    with open(search_object, "r") as file:
        lines = file.read().split("\n")

elif os.path.isdir(search_object):
    input_object = 2

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

    if input_object == 1:
        matched = search_loop(lines)

    elif input_object == 2:
        matched = search_loop(os.listdir(search_object))

print(matched)

Я определил функцию comparison(), чтобы я мог использовать ее позже в скрипте, не повторяя эти строки. Однако тогда мне пришлось присвоить global string в другой функции, так как в противном случае я получаю следующую ошибку. NameError: name 'string' is not defined

Мне интересно, как мне избежать использования глобальной переменной. Я читал, что классы могут часто использоваться, чтобы избежать такого, но я не мог понять, как это было бы полезно здесь. Буду признателен за любые рекомендации по хорошему решению этой задачи.

1 Ответ

0 голосов
/ 20 января 2019

Есть несколько вещей, которые мы можем сделать, чтобы очистить код.В конце мы избавимся от глобальной переменной.

Сделаем данные непротиворечивыми

Вместо того, чтобы ждать до конца, чтобы получить содержимое каталога, мы можем получить его в самом начале,Таким образом, позже мы сможем обрабатывать строки текста и содержимое каталога точно так же.

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)

Дальнейшие улучшения

  1. Обработка ошибок - везде, где что-то может пойти не так, мы можем raiseсоответствующая ошибка (для чего-то, что может быть обработано) или assert (для чего-то, чего мы не ожидаем и не можем реально восстановить).
  2. Модули типа argparse могутупростить ввод аргументов в сценарий, чтобы нам не приходилось жестко кодировать значения переменных.
  3. Вместо того, чтобы принимать «поисковый объект», а затем беспокоиться о том, является ли это файлом или каталогоммы могли бы также прочитать из sys.stdin и оставить его пользователю для генерации текста и передачи его в программу, например: cat words.txt | python script.py для содержимого или ls . | python script.py для файлаs в каталоге.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...