python 函数进阶

匿名函数

语法 lambda 参数:返回结果

顾名思义,匿名函数一般没有函数名,一般用于和高阶函数配合一次性使用

特点:写法简单,常用于做一些简单的处理

举例:

res=sorted([obj1,obj2,obj3],key=lambda x:x.age,reverse=True)
自定义对象时没法排序的,这里使用lambda取出对象的属性返回,作为排序规则

闭包函数

闭包函数的3个特点

1,要用函数嵌套

2,内部函数对外部函数的变量有引用

3,外部函数返回内部函数

闭包函数的使用

1,调用外部函数得到内部函数

2,再调用第一步得到结果(内部函数)得到内部函数的结果

重点:无论闭包函数在何处被调用,包外变量如何改变,内层函数都使用包内的变量

import requests
def outter(url): 
    def get():
        response=requests.get(url)
    return get

baidu=outter('https://www.baidu.com')
baidu()

cnblogs=outter('https://www.cnblogs.com/linhaifeng')
cnblogs()

zhihu=outter('https://zhuanlan.zhihu.com/p/109056932')
zhihu()

闭包函数的作用

本来一个函数就能解决的问题为什么要用一个嵌套函数来解决呢?

上述例子只是为了演示闭包函数的使用过程,在日常业务中我们经常会遇到这种问题:

一个函数已经编写好了,但是后期有些业务需要给该函数添加一些功能(可能需要额外的参数),如果直接修改原函数代码,对于其他已经使用了该函数的业务将是一场灾难,如果每次调用函数之前或之后手动调用额外的函数有太麻烦了,所以我们需要一种不改变函数源码的解决方案,这就是闭包函数.

闭包函数的作用:在不改变一个函数源代码的情况下,给该函数加入新的功能

需求:在不修改index函数的源代码以及调用方式的前提下为其添加统计运行时间的功能

import time

def index(x,y,z):
    time.sleep(3)
    print('index %s %s %s' %(x,y,z))

def home(name):
    time.sleep(2)
    print('welcome %s to home page' %name)

def outter(func):
    def wrapper(*args,**kwargs):
        start=time.time()
        res=func(*args,**kwargs)
        stop=time.time()
        print(stop - start)
        return res
    return wrapper

使用:
# 1,获取内层函数,home这个名字指向的wrapper函数的内存地址
home=outter(home)
# 2,调用home函数,此时的home的指向已经变了
res=home('tom') 

装饰器

作用:简化闭包函数的使用方式

装饰器用到的一些概念

装饰器是参数,函数,函数的嵌套定义,函数对象,名称空间和作用域,闭包函数等概念组合起来形成的一种产物

1,可变长参数 args, kwargs*

如何将传入一个函数的任意个任意形态的实参原封不动地传给另一个函数

便是通过*args与**kwargs在形参位置和实参位置的不同作用完成的

def index(x,y):
    print(x,y)
def wrapper(*args,**kwargs):
    index(*args,**kwargs) # 最终传参为index(y=222,x=111)
wrapper(y=222,x=111)

2 名称空间与作用域

闭包中特殊的变量与名称空间的关系:

名称空间的的"嵌套"关系是在函数定义阶段,即检测语法的时候确定的

即需要调用的变量在定义阶段就确定了取变量的名称空间的顺序(不会确定值)

3 函数对象

函数可以被当做参数传入

函数可以当做返回值返回

def index():
    return 123

def foo(func):
    return func

print(foo(index))

4 函数的嵌套

def outter(func):
    def wrapper():
        pass
    return wrapper

5 传参的方式

传参的方式一:通过参数的形式为函数体传值

def wrapper(x):
    print(1)
    print(2)
    print(3)
    print(x)

wrapper(1)
wrapper(2)
wrapper(3)

传参的方式二:通过闭包的方式为函数体传值

def outter(x):
    def wrapper():
        print(1)
        print(2)
        print(3)
        print(x)
    return wrapper # return outter内的wrapper那个函数的内地址

f1=outter(1)
f1()
f2=outter(2)
f2()
f3=outter(3)
f3()

6 装饰器的主体

装饰器的主体是由闭包函数组成的

def outter():
    x=111
    def wrapper():
        print(x)
    return wrapper

f=outter()  # 此处f可以为任何名字,包括wrapper=outter()
f()			# wrapper(),这个与原函数同名,但是互不影响,这是名称空间的特性
# f为全局空间中的名字,而wrapper为局部空间中的名字

装饰器的使用

语法糖

在函数定义阶段用@+装饰器名字

import time
def timmer(func):
    def wrapper(*args,**kwargs):
        start=time.time()
        res=func(*args,**kwargs)
        stop=time.time()
        print(stop - start)
        return res
    return wrapper


# 在被装饰对象正上方的单独一行写@装饰器名字
@timmer #@timmer等价于 index=timmer(index)
def index(x,y,z):
    time.sleep(3)
    print('index %s %s %s' %(x,y,z))

@timmer # home=timmer(ome)
def home(name):
    time.sleep(2)
    print('welcome %s to home page' %name)

调用:和正常的调用方法一样
index(x=1,y=2,z=3)
home('tom')

高度伪装

虽然使用方法一样,但是两个函数的元信息还是不一样,此时可以使用python提供的装饰器wraps

from functools import wraps

def outter(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        """这个是主页功能"""
        res = func(*args, **kwargs) # res=index(1,2)
        return res
    return wrapper
#@wraps的作用就是将原函数的元属性赋值给wrapper函数
#    wrapper.__name__ = func.__name__
#    wrapper.__doc__ = func.__doc__

@outter 
def index(x,y):
    """这个是主页功能"""
    print(x,y)


print(index.__name__)	#这样两个函数的元属性就一样了
print(index.__doc__) 	

无参装饰器

只需传入被装饰函数所需要的参数,不需要额外参数

上面的例子就是无参装饰器

带参装饰器

需要被装饰函数所需要的以外参数,这个参数不是给被装饰函数使用的

def use_logging(level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if level == "warn":
                logging.warn("%s is running" % func.__name__)
            elif level == "info":
                logging.info("%s is running" % func.__name__)
            return func(*args)
        return wrapper

    return decorator

@use_logging(level="warn")
def foo(name='foo'):
    print("i am %s" % name)

foo()

上面的 use_logging 是允许带参数的装饰器。它实际上是对原有装饰器的一个函数封装,并返回一个装饰器。我们可以将它理解为一个含有参数的闭包。当我 们使用@use_logging(level="warn")调用的时候,Python 能够发现这一层的封装,并把参数传递到装饰器的环境中。

类装饰器

类也可以用来构建装饰器。那我们现在以一个类而不是一个函数的方式,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器时,用__init__接参数,依靠类的__call__方法响应调用,当使用 @ 形式将装饰器附加到函数上时,就会调用__call__方法。

class Foo(object):
    def __init__(self, func):
        self._func = func

    def __call__(self):
        print ('class decorator runing')
        self._func()
        print ('class decorator ending')

@Foo
def bar():
    print ('bar')

bar()

类装饰器拓展功能就变得极其简单了,使用类的继承派生就搞定

装饰器执行顺序

效果:近水楼台先得月,谁离被装饰函数进,谁先被执行

@a
@b
@c
def f ():
    pass

函数递归

1:递归的定义
函数的递归调用:是函数嵌套调用的一种特殊形式
具体是指:
在调用一个函数的过程中又直接或者间接地调用到本身

直接调用本身

 def f1():
     print('是我是我还是我')
     f1()
f1()

间接接调用本身

 def f1():
     print('===>f1')
    f2()

 def f2():
     print('===>f2')
     f1()

 f1()

一段代码的循环运行的方案有两种

# 方式一:while、for循环
# while True:
#     print(1111)
#     print(2222)
#     print(3333)

# 方式二:递归的本质就是循环:
# def f1():
#     print(1111)
#     print(2222)
#     print(3333)
#     f1()
# f1()

2:需要强调的的一点是,递归调用不应该无限地调用下去,必须在满足某种条件下结束递归调用

# n=0
# while n < 10:
#     print(n)
#     n+=1


# def f1(n):
#     if n == 10:
#         return
#     print(n)
#     n+=1
#     f1(n)
#
# f1(0)

3:递归的两个阶段
回溯:一层一层调用下去
递推:满足某种结束条件,结束递归调用,然后一层一层返回

4:递归的应用

l=[1,2,[3,[4,[5,[6,[7,[8,[9,10,11,[12,[13,]]]]]]]]]]

def f1(list1):
    for x in list1:
        if type(x) is list:
            # 如果是列表,应该再循环、再判断,即重新运行本身的代码
            f1(x)
        else:
            print(x)

f1(l)

5 : 递归和迭代的区别

递归:函数递归是不断的重复函数的代码,若想获得不同的结果一般都要改变参数,否则每次只能得到相同的结果

迭代:每一次重复是基于上一次的结果的重复

posted @ 2020-03-19 20:55  Franciszw  阅读(299)  评论(0编辑  收藏  举报