Python list variable issue

61 阅读2分钟

该问题是关于 Python 中一个 list 变量的 bug。当使用该脚本对目录进行映射的时候,如果只对一个目录进行操作,那么脚本可以正常工作。但是,如果对两个目录进行操作,那么第二个目录的列表中包含了第一个目录的所有数据。

2、解决方案

这个问题的原因是该脚本在 list_of_files 函数的定义中使用了可变的参数作为默认值。导致每次调用 list_of_files 函数时,都会使用同一个列表对象。为了解决这个问题,需要在 list_of_files 函数的参数中使用哨兵值。

def list_of_files(self, dir_name, traversed=None, results=None):
    if traversed is None:
        traversed = []
    if results is None:
        results = []
    # rest of your method...

这样,每次调用 list_of_files 函数时,都会创建一个新的列表对象,而不是重复使用同一个列表对象。

代码例子

以下是使用哨兵值后的代码示例:

import os 
import hashlib
import platform
import sys
import argparse
import HTML


class Map(object):

    #init function
    def __init__(self,param,out_path):
        self.param_list = param
        self.slash = self.slash_by_os()
        self.result_list = []
        self.os = ""
        self.index = 0
        self.html_out_path = out_path

    def export_to_HTML(self):
        html_code = HTML.table(self.result_list,header_row=self.param_list)
        f = open(self.html_out_path,'w')
        f.write(html_code + "<p>\n")
        f.close()


    def calc_md5(self,file_path):
        hash = hashlib.md5()
        with open(file_path, 'rb') as file_to_check:
            for chunk in iter(lambda: file_to_check.read(4096), ''):    
                hash.update(chunk)

        return hash.hexdigest()

    def slash_by_os(self):
        general_id = platform.system()
        actual_os = ""

        if general_id == "Darwin" or general_id == "darwin":
            actual_os = "UNIX"
        elif general_id == "Linux" or general_id == "linux":
            actual_os = "UNIX"
        elif general_id  == "SunOS":
            actual_os = "UNIX"
        elif general_id == "Windows" or general_id == "windows":
            actual_os = "WIN"
        else:
            actual_os = general_id

        if actual_os == "UNIX":
            return '/'
        elif actual_os == "WIN":
            return '\'
        else:
            return '/'

        self.os = actual_os

    def what_to_do(self,new_dir):
        act = []
        act.append(new_dir[:-1])
        for param in self.param_list:
            if param == "md5":
                x = self.calc_md5(new_dir[:-1])
                act.append(x)   

        return act

    def list_of_files(self ,dir_name ,traversed = None, results = None): 

        if traversed is None:
            traversed = []
        if results is None:
            results = []
        
        dirs = os.listdir(dir_name)
        if dirs:
            for f in dirs:
                new_dir = dir_name + f + self.slash
                if os.path.isdir(new_dir) and new_dir not in traversed:
                    traversed.append(new_dir)
                    self.list_of_files(new_dir, traversed, results)
                else:
                    try:
                        act = self.what_to_do(new_dir)
                        results.append(act)
                    except Exception as e :
                        print "%s excepted %s, couldent read" % (new_dir,e)
        self.result_list = results
        return results

def parse_args():
    desc = "DirMap 1.0"
    parser = argparse.ArgumentParser(description=desc)
    parser.add_argument('-p1','--ogpath', help='Path To Original Directory', required=True)
    parser.add_argument('-p2','--modpath', help='Path To Modified Directory', required=True)


    args = vars(parser.parse_args())


    params = ['path','md5']
    return args,params


def main():
    args , params = parse_args() 

    og_dir_path = args['ogpath']
    og_map = Map(params,"og.html")
    og_list = og_map.list_of_files(og_dir_path)
    og_map.export_to_HTML()

    mod_dir_path =args['modpath']
    mod_map = Map(params,"mod.html")
    mod_list = mod_map.list_of_files(mod_dir_path)
    mod_map.export_to_HTML()



main() 

这个示例中,list_of_files 函数使用哨兵值作为默认参数,这样每次调用该函数时,都会创建一个新的列表对象。因此,第二个目录的列表中不会包含第一个目录的所有数据。