Python的递归,几种炫技的用法
有个学员在提问python的递归实现,具体题目是用递归的方式,实现指定目录下面所有文件的size统计。自己写了写,发现这个题目还挺有意思的。
代码如下:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from multiprocessing.dummy import Pool as ThreadPool
import os
# 我自己写各种python代码的一个目录,你没看错,我平时用Linux办公,因为逼格高 ʅ(´◔౪◔)ʃ
path = "/home/david/Code/Python"
def dir_size(d):
"""
递归版本.
这是最基础的版本,没啥好说的,需要注意的是,既然用递归,
就不要考虑全局变量啥的了,累计的size,直接作为返回值就好了。
"""
# 读取文件信息
dlist = os.listdir(d)
# 遍历所有内容
allsize = 0
for i in dlist:
# 为遍历的文件添加目录
file = os.path.join(d, i)
# 判断是否是文件
if os.path.isfile(file):
m = os.path.getsize(file)
allsize += m
# 判断是否是目录
if os.path.isdir(file):
# 调用自己
allsize += dir_size(file)
return allsize
print('dir size:\t{}'.format(dir_size(path)))
def dir_size(d):
"""
递归comprehension版本.
这个是逼格稍微高一点的版本,这里的重点就是comprehension了。
"""
dlist = [os.path.join(d, i) for i in os.listdir(d)]
allsize = sum([os.path.getsize(file)
for file in dlist if os.path.isfile(file)])
allsize += sum([dir_size(file)
for file in dlist if os.path.isdir(file)])
return allsize
print('dir size:\t{}'.format(dir_size(path)))
def dir_size(d):
"""
递归comprehension版本.
另一种蛋疼的写法, 吓唬人玩行,一般别吃饱了撑的用这种写法.
"""
return sum([dir_size(f) if os.path.isdir(f) else os.path.getsize(f)
for f in [os.path.join(d, i) for i in os.listdir(d)]])
print('dir size:\t{}'.format(dir_size(path)))
def dir_size(d):
"""
递归map filter版本.
简洁的一逼,刚开始没想起来。
"""
dlist = [os.path.join(d, i) for i in os.listdir(d)]
allsize = sum(map(os.path.getsize, filter(os.path.isfile, dlist)))
allsize += sum(map(dir_size, filter(os.path.isdir, dlist)))
return allsize
print('dir size:\t{}'.format(dir_size(path)))
def dir_size(d):
"""
多线程递归版本.
1,在这里,这个版本反而比单线程的慢,因为我本地是SSD,而且目录也没有大到离谱,线程切换的
开销已经完全抵消了多线程的IO效率优势。如果是统计NFS,大规模的磁盘阵列啥的,可能多线程
会有一些微薄的优势。
2,因为有递归,所以不能用多进程。python里面的子进程不能再有子进程,会报错的。不要在有递归
的场合使用多进程,除非小心的设计一下。
这里其实还有一种思路是先用walk列出所有的文件目录,再用多线程或者多进程统计文件大小,但
是列出所有目录这个动作本身就要遍历所有文件了,所以这种做法在这个场景下其实效率不高。
"""
dlist = [os.path.join(d, i) for i in os.listdir(d)]
allsize = sum([os.path.getsize(file)
for file in dlist if os.path.isfile(file)])
p = ThreadPool()
ret = p.map(dir_size, [file for file in dlist if os.path.isdir(file)])
p.close()
p.join()
allsize += sum(ret)
return allsize
print('dir size:\t{}'.format(dir_size(path)))
def dir_size(d):
"""
walk版本.
这个版本就没有递归了,有点离题,但是递归这个东西本身就有点反人类。
没想起来怎么把walk的循环用comprehension代替,有兴趣的亲可以试一下。
"""
allsize = 0
for r, ds, fs in os.walk(d):
allsize += sum(os.path.getsize(file) for
file in [os.path.join(r, f) for f in fs])
return allsize
print('dir size:\t{}'.format(dir_size(path)))
总结
忘了哪里看到得了,python里面,尽量避免for/while循环的使用,if判断也尽量少用,这样不仅代码更简洁,代码的运行效率也会更高(目前没觉得)。
Python里面有很多很方便的用法,部分支持函数式编程。跟java/C++之类比较的强类型静态语言相比,在很多场景下方便很多(虽然性能跟屎一样的╮(╯_╰)╭)。这些便利也造就了Python庞大的生态,目前还没看到哪个语言的生态能像python这样子的(个人观点,欢迎抬杠)。