以Python的方式编程

6 投票
3 回答
929 浏览
提问于 2025-04-15 23:22

我刚刚在大学里学习了半个学期的Python,感觉非常不错。现在我希望能得到一些建议,教我怎么写得更“Pythonic”,也就是更符合Python风格的代码。

这是我最近一个作业里的__init__类。当时我在写的时候,想着怎么用lambda表达式或者更整洁、更高效的方式来重写它,但时间不够了。

def __init__(self, dir):

    def _read_files(_, dir, files):

        for file in files:

            if file == "classes.txt":
                class_list = readtable(dir+"/"+file)
                for item in class_list:
                    Enrol.class_info_dict[item[0]] = item[1:]
                    if item[1] in Enrol.classes_dict:
                        Enrol.classes_dict[item[1]].append(item[0])
                    else:
                        Enrol.classes_dict[item[1]] = [item[0]]

            elif file == "subjects.txt":
                subject_list = readtable(dir+"/"+file)
                for item in subject_list:
                    Enrol.subjects_dict[item[0]] = item[1]

            elif file == "venues.txt":
                venue_list = readtable(dir+"/"+file)
                for item in venue_list:
                    Enrol.venues_dict[item[0]] = item[1:]

            elif file.endswith('.roll'):
                roll_list = readlines(dir+"/"+file)
                file = os.path.splitext(file)[0]
                Enrol.class_roll_dict[file] = roll_list
                for item in roll_list:
                    if item in Enrol.enrolled_dict:
                        Enrol.enrolled_dict[item].append(file)
                    else:
                        Enrol.enrolled_dict[item] = [file]


    try:
        os.path.walk(dir, _read_files, None)
    except:
        print "There was a problem reading the directory"

你可以看到,这段代码有点臃肿。如果有人有时间或者愿意的话,我非常感谢能给我一些Python编程的最佳实践建议。

谢谢!

3 个回答

2

如果你想让你的脚本长时间使用(有些人会说是非常长的时间),还有一个重要的点就是不要在新代码中使用已经不推荐使用的函数:

os.path.walk 在 Python 3.x 中已经不再使用了。现在你可以用 os.walk 来代替。不过 os.walkos.path.walk 是不一样的:它的参数中不接受处理函数。所以,修改你的代码不仅仅是换个名字这么简单,还需要做一些额外的调整。

3

除了orangeoctopus提到的使用 setdefault 的建议,你还可以把if-else的结构改成一个调度器,这是一种常用的方法来替代那些很大的if-else和switch语句。

# list of 2-tuples: (bool func(string filename), handler_function)
handlers = [
  ((lambda fn: fn == "classes.txt"), HandleClasses),
  ((lambda fn: fn == "subjects.txt"), HandleSubjects),
  ((lambda fn: fn.endswith(".roll")), HandleRoll)
]

然后执行

for filename in files:
  for matcher, handler in handlers:
    if matcher(filename):
      handler(filename)
      break
5

有几个小建议可以让你的代码更简洁:

可以使用字典的setdefault方法。如果某个键不存在,它会把这个键设置为你提供的默认值,然后返回这个值。如果这个键已经存在,它就会忽略你提供的默认值,直接返回字典里原本的值。这样可以避免写那些繁琐的if语句。

Enrol.venues_dict.setdefault(key, []).append(file)

>>> x = {}
>>> x.setdefault(99, []).append(5) 
>>> x.setdefault(99, []).append(6)
>>> x
{99: [5, 6]}
>>> x.setdefault(100, []).append(1)
>>> x
{99: [5, 6], 100: [1]}

另外一个建议是使用os.path.join来创建文件路径。这样比直接拼接字符串要安全得多。

os.path.join(dir, file)

除此之外,我觉得在风格上看起来不错。

撰写回答