进阶学Python:Python的递归函数和装饰器(重点)! | 八月更文挑战

1,094 阅读9分钟

这是我参与8月更文挑战的第18天,活动详情查看:8月更文挑战

📖前言

三毛曾经感慨:

“心之何如,有似万丈迷津,横亘千里,其中并无舟子可以渡人,除了自渡,他人爱莫能助。”——生命的洪流中,终其一生,我们都在学会怎么做自己的摆渡人。

关于安装和汉化可以观看博主的这篇文章《VsCode下载安装及汉化 》以及Python系列:windows10配置Python3.0开发环境!,安装完毕重启VsCode!


🚀Come on!什么是递归:

在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。记住哦->在函数内部调用其他函数不是函数的嵌套,而在函数内部定义子函数才是函数的嵌套。

  • 递归的特性:
  1. 递归函数必须有一个明确的结束条件。

  2. 每进入更深一层的递归时,问题规模相对于上一次递归都应减少

  3. 相邻两次重复之间有紧密的联系,前一次要为后一次做准备(通常前一次的输出就作为后一次的输入)。

  4. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。

例如下面的案例

'''
@Descripttion: 这是一个文件头注释!
@version: 1.0.0
@Company: DCIT-SH
@Author: Sunny Chen
@Date: 2019-10-09 18:20:20
@LastEditors: Sunny Chen
@LastEditTime: 2019-10-15 09:58:20
'''

import sys
#通过循环来实现叠加
def sum1(n):
    '''
to n,The sum function
    '''
    sum = 0
    for i in range(1,n + 1):
        sum += i
    return sum

#通过函数的递归来实现叠加
def sum2(n):
    '''
to n,The sum function
    '''
    if n > 0:
        return n + sum2(n - 1) #调用函数自身
    else:
        return 0

print("循环叠加-->",sum1(50))
print("递归叠加-->",sum2(50))

#两者实现的效果均是:1275

两者都可以实现叠加的效果。后者相对前者有什么优缺点?


✨递归函数有啥优缺点:

递归函数的优点:定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。

递归函数的缺点:使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。

把上面的递归求和函数的参数改成10000就导致栈溢出!

当然也可以通过尾递归来对递归的栈溢出进行优化。这里就不详细介绍了。请参考(Python开启尾递归优化!):www.open-open.com/lib/view/op…


🤳通过实例来介绍函数递归:

'''
@Descripttion: 这是一个文件头注释!
@version: 1.0.0
@Company: DCIT-SH
@Author: Sunny Chen
@Date: 2019-10-09 18:20:20
@LastEditors: Sunny Chen
@LastEditTime: 2019-10-15 10:20:37
'''

# 假如街上有这么一群人,我想问知道长沙的小吃街在哪。其中只有二狗子(大吃货)知道在哪。
names = ["小佳佳", "刘二", "张三", "李四", "二狗子", "王五"]
print(names)
# 问路

def ask_way(lis):
    # 街上就我一个人的情况下
    if len(lis) == 1:
        return "没有人知道这个地方在哪。"
    # 街上不只我一个人的情况下,先问第一个人
    name = lis.pop(0)
    # 如果这个人是二狗子,那么问到路,递归结束
    if name == "二狗子":
        return "二狗子说:长沙的小吃街在天心区南门口!!"
    # 如果这个人不知道路,就去问下一个人,下一个人就好把结果告诉这个人。
    print("<%s>问:你[%s]好,请问你知道长沙的小吃街在哪吗?" % (name, lis[0]))
    print("不好意思,我帮您问一下%s,他可能会知道!!\n" % lis[0])
    res = ask_way(lis)
    if name != "小佳佳":
        print("<%s>说:%s" % (name, res))
    return res

ask_way(names)

# 输出结果为:
"""
['小佳佳', '刘二', '张三', '李四', '二狗子', '王五']
<小佳佳>问:你[刘二]好,请问你知道长沙的小吃街在哪吗?
不好意思,我帮您问一下刘二,他可能会知道!!

<刘二>问:你[张三]好,请问你知道长沙的小吃街在哪吗?
不好意思,我帮您问一下张三,他可能会知道!!

<张三>问:你[李四]好,请问你知道长沙的小吃街在哪吗?
不好意思,我帮您问一下李四,他可能会知道!!

<李四>问:你[二狗子]好,请问你知道长沙的小吃街在哪吗?
不好意思,我帮您问一下二狗子,他可能会知道!!

<李四>说:二狗子说:长沙的小吃街在天心区南门口!!
<张三>说:二狗子说:长沙的小吃街在天心区南门口!!
<刘二>说:二狗子说:长沙的小吃街在天心区南门口!!
"""

递归值返回时返回到上一层,而其他的层在执行的过程中,均在等待答案。快去动手试试吧!


🎇Come on!什么是装饰器?:

装饰器(Decorator):首先我们需要知道装饰器本身就是一个函数,而这个函数存在的意义就是为其他函数添加附加功能的。

而从某种程度上来说:装饰器并非一个功能特性,它只是一个语法糖,因为从本质上来讲:它实现的是将一个函数或者对象作为参数传递给另外一个函数或者对象,最后再返回新的函数对象,而这个新的函数或者对象内部既实现了原函数的功能,也为其附加了新的功能。

在实现目的-->为其他函数添加附加功能,在这个前提下还需要遵循两个原则,即:

  • 一、不修改被修饰函数的源代码。
  • 二、不修改被修饰函数的调用方式。

其语法是:

装饰器以 “@”开头,在定义的函数的上一行调用即加上“@xxx”,xxx为装饰器的函数名,来为这个函数来装饰附加功能。例如:

'''
@Descripttion: 这是一个文件头注释!
@version: 1.0.0
@Company: DCIT-SH
@Author: Sunny Chen
@Date: 2019-10-15 13:42:21
@LastEditors: Sunny Chen
@LastEditTime: 2019-10-16 15:46:50
'''

#装饰器deco
def deco1(func):
    pass
def deco2(func):
    pass    

@deco1  #@+装饰器的函数名,写在被装饰函数的上一行
@deco2  #一个被修饰函数可以有多个装饰器
#被修饰函数test
def test():
    pass

🎊Come on!为什么要使用装饰器?

装饰器的作用:(每个分开运行)

'''
@Descripttion: 这是一个文件头注释!
@version: 1.0.0
@Company: DCIT-SH
@Author: Sunny Chen
@Date: 2019-10-15 13:42:21
@LastEditors: Sunny Chen
@LastEditTime: 2019-10-16 15:48:59
'''
#假如你的老板给了你这样的一段代码,让你计算其运行的时长。

import time
def test():
    time.sleep(1)
    print("hello world")

#1、当然最直接的方式就是篡改源代码,例如:
import time
def tests():
    start_time =time.time()
    time.sleep(1)
    print("hello world")
    end_time =time.time()
    print("costed time-->%s",%(end_time-start_time))

#2、随后老板说了:“这是咱们公司的核心代码,不能修改原代码”,随后你可能会想到高阶函数来实现,将原函数作为参数传入另外一个函数来实现:
import time
def test():
    time.sleep(1)
    print("hello world")

def deco(func):
    start_time =time.time()
    func()
    end_time = time.time()
    print("costed time-->%s"%(end_time-start_time)
#调用
deco(test)


#3、当你将这段代码交给老板时,老板又说:“目前这段代码以及投入使用了,怎么能随便修改调用方式呢?”这时候你又苦恼了,这时候你又想到了高阶函数中的将函数作为返回值
import time
def test():
    time.sleep(1)
    print("hello world")

def deco(func):
    start_time =time.time()
    func()
    end_time = time.time()
    print("costed time-->%s"%(end_time-start_time)
    return func
#调用
test=deco(test)
test()


#4、这时候你又会发现,原函数执行了两次,这时候老板又不满意了,“卧槽,这原函数怎么执行了两次啊”,于是又让你改,这时候你看到隔壁老王在写装饰器,你一下子惊为天人,于是有了下面这段代码:
import time
def test():
    time.sleep(1)
    print("hello world")

def deco(func):
    def wrapper(func):
        start_time =time.time()
        func()
        end_time =time.time()
        print("costed time-->%s"%(end_time-start_time)
    return wrapper

test = deco(test)
test()


#其实这时候deco函数就是装饰器的最原始从状态。用装饰器来实现就是这样的。
import time
#装饰器deco
def deco(func):
    def wrapper(func):
        start_time =time.time()
        func()
        end_time =time.time()
        print("costed time-->%s"%(end_time-start_time)
    return wrapper
#被修饰函数
@deco  #@deco <==> test =deco(test)
def test():
    time.sleep(1)
    print("hello world")
#调用
deco()

从上面例子中可以看出,使用装饰器可以在不修改被修饰函数源代码以及不改变被修饰函数的调用方式的前提下,为函数添加新的附加功能。


💕Come on!如何来实现装饰器?

其实在实现装饰器之前,我们需要了解 高阶函数、函数的嵌套和闭包等概念。当然这些我会在其他的文章中介绍。

带参数的被修饰函数以及有返回值的被修饰函数如何实现装饰器。

代码分了1-3(分开运行)

'''
@Descripttion: 这是一个文件头注释!
@version: 1.0.0
@Company: DCIT-SH
@Author: Sunny Chen
@Date: 2019-10-15 13:42:21
@LastEditors: Sunny Chen
@LastEditTime: 2019-10-16 15:54:58
'''

#1、装饰器的基本实现
import time
def timmer(func):
    def warpper(): #为了避免函数运行两次而需要的中间函数
        start_time= time.time()
        func()
        end_time =time.time()
        print("cost time--->:",end_time-start_time)
    return warpper

@timmer #@timmer <-->  test=timmer(test)
def test():
    time.sleep(1)
    print("in the test.")

test() #由于test=timmer(test),而执行timmer得到的返回值为wrapper;故其实调用test函数,本质上调用的是timmer下的子函数warpper。

#2、若被修饰函数带有返回值
import time
def timmer(func):
    def warpper(): #为了避免函数运行两次而需要的中间函数
        start_time= time.time()
        res=func()
        end_time =time.time()
        print("cost time--->:",end_time-start_time)
        return res
    return warpper

@timmer #@timmer <-->  test=timmer(test)
def test():
    time.sleep(1)
    print("in the test.")
    return "This is test' result"

test() #由于调用test()本质是执行wrapper(),故执行test()的返回值即为wrapper()的返回值即可。而需要的是被修饰函数为返回值,故把res作为wrapper的返回值即可。

#3、如被修饰函数带参数
import time
def timmer(func):
    def warpper(*args,**kwargs): #为了避免函数运行两次而需要的中间函数
        start_time= time.time()
        res=func(*args,**kwargs)
        end_time =time.time()
        print("cost time--->:",end_time-start_time)
        return res
    return warpper

@timmer #@timmer <-->  test=timmer(test)
def test(name):
    time.sleep(1)
    print("in the test.my name is %s."%name)
    return "This is test' result"

test(name)

到这里:进阶学Python:Python的递归函数和装饰器(重点)!分享完毕了,快去试试吧!


🎉最后

  • 更多参考精彩博文请看这里:陈永佳的博客

  • 喜欢博主的小伙伴可以加个关注、点个赞哦,持续更新嘿嘿!