如何在python中避免全局变量和重复

时间:2019-01-20 01:48:21

标签: python global-variables

我编写了一个脚本,该脚本遍历字符串列表,并在文件的(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 :(得分:1)

我们可以采取一些措施来清理代码。最后,我们将摆脱全局变量。

使数据保持一致

我们不必等到最后才获得目录内容,而是可以从一开始就获得它。这样,以后我们就可以完全相同地对待文本行和目录内容。

const options = {
  disableStorageKey: true,
  onFileSelected: updateStorePath,
  fromSources: ['local_file_system', 'dropbox'],
  storeTo: {
    access: 'private',
    container: 'myContainer',
    location: 's3',
    region: 'us-east-2',
    path: ''
  }
};

function updateStorePath (file) {
  file.size # I can access size
  < not sure how to proceed >
}

有意义时,请尽早选择正确的操作

之前,我们每次都要检查执行哪种操作(完全匹配或包含)。相反,我们可以在获得用户输入时选择正确的选项,并将其分配给单个名称以供以后使用。

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__'

进一步的改进

  1. 错误处理-无论哪里出了问题,我们都可以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(对于我们不希望发生且无法真正恢复的错误)来自)。
  2. argparse这样的模块可以更轻松地将参数带入脚本,因此我们不必对变量值进行硬编码。
  3. 我们也不必担心“搜索对象”是文件还是目录,而可以从assert中读取内容,并留给用户以某种方式生成文本并将其通过管道传递给程序,例如:sys.stdin表示目录中的内容或cat words.txt | python script.py表示目录中的文件。