Python的递归,几种炫技的用法

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这样子的(个人观点,欢迎抬杠)。

### 回答1: 递归是一种解决问题的方法,它通过将问题分解为更小的、相同的子问题来解决问题。在 Python 中,可以使用递归函数来实现递归处理。 递归函数是在函数内部调用自身的函数。在递归函数中,需要设置一个递归终止条件,以避免函数无限递归递归终止条件应该是可以直接得到答案的情况,例如,当问题规模缩小到一定程度时,可以直接计算出答案。 以下是一个使用递归函数实现阶乘的例子: ``` def factorial(n): if n == 1: return 1 else: return n * factorial(n-1) # 测试 print(factorial(5)) ``` 在这个例子中,`factorial` 是一个递归函数,用于计算阶乘。当 `n == 1` 时,递归终止,返回结果 1。否则,递归调用 `factorial(n-1)` 并将结果乘以 `n`,直到递归终止。 递归函数的优点是它可以使代码更加简洁和易于理解。然而,递归函数也存在一些缺点,例如,它可能会导致堆栈溢出,因为每个函数调用都需要在堆栈上分配一些内存。因此,在使用递归函数时,需要谨慎处理递归终止条件,以避免出现无限递归的情况。 ### 回答2: Python递归是一种函数调用自身的方式,用于解决可以被拆分为相同类型子问题的问题。使用递归可以简化代码,并且能够很好地解决一些数学问题,如阶乘、斐波那契数列等。 在使用递归时,需要注意以下几点: 1. 定义递归的边界条件:递归必须要有终止条件,否则会形成无限递归导致程序崩溃。例如,在计算阶乘时,边界条件可以是n=0或n=1时返回1。 2. 将问题拆分为子问题:递归的过程中,需要将原问题拆分为一个或多个同类型的子问题,并逐个解决子问题。例如,在计算斐波那契数列时,可以将n的计算分为计算n-1和n-2的子问题。 3. 适当地调用自身:在求解子问题时,需要调用自身来解决较小规模的问题。递归函数的调用需要满足问题规模逐渐变小的条件,否则可能导致栈溢出。例如,计算斐波那契数列时,可以使用递归公式F(n) = F(n-1) + F(n-2)来逐步求解。 递归的优点在于可以使代码更加简洁且易于理解,但同时也有一定的性能损耗。大量的递归调用会导致函数堆栈的不断增长,可能会消耗较多的内存和时间。 因此,在使用递归时需要注意边界条件和递归的终止条件,避免出现无限递归的情况。此外,也可以考虑使用非递归的方式来实现相同的功能,以提高代码的执行效率。 ### 回答3: Python递归是一种函数调用自身的技术。递归处理是指在解决问题时,使用函数自身来进行多次循环,从而达到简化程序的目的。 递归处理的优点是可以简化代码并解决一些复杂的问题。例如,可以使用递归来计算斐波那契数列,即每个数是前两个数的和。通过使用递归,可以将问题简化为计算前两个斐波那契数列的和,并不断地重复这个过程直到得到所需的数。 然而,递归也有一些缺点。由于递归调用自身,会导致函数多次被调用,从而可能引起内存溢出的问题。此外,递归的运行速度通常较慢,因为每次调用都需要保存当前执行状态并在下次调用时恢复。 在编写递归函数时,需要考虑以下几个要点。首先,确定基本情况,即停止递归的条件。其次,将问题分解为更小的子问题,并使用递归调用解决这些子问题。最后,确保每次递归调用都朝着基本情况靠近,避免出现无限循环的情况。 总之,递归处理是一种强大的技术,可以简化编程过程并解决一些复杂的问题。然而,需要注意在编写递归函数时避免出现内存溢出和无限循环的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_dwSun_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值