学习目标:

Python学习九、


学习内容:

1、返回函数
2、匿名函数
3、装饰器
4、偏函数


1、返回函数

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回,当我们调用某一返回函数时,调用的不是结果而是函数

实现一个可变参数的求和

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum
f = lazy_sum(1, 3, 5, 7, 9)
print(f)
输出:
<function lazy_sum.<locals>.sum at 0x000001FEDE00A040>
调用函数f时,才真正计算求和的结果
print(f())
输出:
25

调用返回函数时,每次调用都会返回一个新的函数,即使传入相同的参数,调用结果也可能不同

  • 闭包
    返回的函数在其定义内部引用了局部变量,一个函数返回了一个函数后,其内部的局部变量仍被新函数引用锁定在内部函数中,这就叫闭包
  • 一个函数在for循环里面,当它只是定义而没有调用时,是不会被运行的

def f()函数在for运行的过程中并未使用,而是到程序的print(f1()、print(f2()、print(f3()处时才调用f()函数,此时i已经是3了

def count():
    fs = []
    for i in range(1, 4):
        def f():
             return i*i
        fs.append(f)
    return fs

f1, f2, f3 = count()
print(f1()
print(f2()
print(f3()
输出:
9
9
9

闭包的作用:实现数据锁定,提高程序的稳定性,一般与装饰器一起使用

def count():
    fs = []
    for i in range(1, 4):
        def f(i):
            def a():
                return i*i
            return a
        fs.append(f(i))
    return fs
f1, f2, f3 = count()
print(f1())
print(f2())
print(f3())
输出:
1
4
9
  • 实例:利用闭包返回一个计数器函数,每次调用它返回递增整数
def a():
    f = []
    def b():
        f.append(1)
        return len(f)
    return b
i = a()
print(i())
print(i())
print(i())
print(i())
print(i())
输出:
1
2
3
4
5

2、匿名函数

关键字lambda表示匿名函数,冒号前面的x表示函数参数
匿名函数有个限制,就是只能有一个表达式
不用写return,返回值就是该表达式的结果
匿名函数没有名字,不必担心函数名冲突 传入

  • 以map()函数为例,计算f(x)=x2时,除了定义一个f(x)的函数外,还可以直接传入匿名函数

其中lamdba匿名函数就是 :
def f(x):
return x*x

for i in list(map(lambda x:x*x, [1, 2, 3, 4, 5])):
    print(i)
输出:
1
4
9
16
25
  • 匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数
def a(i,j,n):
    print('i=',i)
    print('j=',j)
    print('i-j=',n(i,j))
a(10,9,lambda i,j: i - j)
输出:
i= 10
j= 9
i-j= 1
  • 实例:用匿名函数输出20以内的奇数

一般方法:

def jishu(n):
    return n%2==1
L = list(filter(jishu, range(1, 20)))
print(L)
输出:
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]

匿名函数

L = list(filter(lambda x:x%2==1, range(1, 20)))
print(L)
输出:
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]

3、装饰器

函数是一个对象,它也具有一些属性可以被改变,而且函数对象可以被赋值给变量,通过变量也能调用该函数

  • 一个简单的求平方
def f(x):
    return x * x
d = f
print(d(9))
输出:
81

__name__属性可以拿到函数的名字

def f(x):
    return x * x
d = f
print(d.__name__)
输出:
f
  • 在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator),本质上decorator就是一个返回函数的高阶函数

Python的@语法把函数放在需要运用的地方
wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用

- 输出一万个hello world!!!,并显示运行时间个和个数
import time
def help(func):
    def wrapeer(*args):
        t1 = time.time()
        rseult = func(*args)
        t2 = time.time()
        print('运行时间为:', t2-t1)
        return  rseult
    return wrapeer

@help
def prinre(num):
    count = 0
    for i in range(1, num):
        print('hwllo world!!!')
        count = count +1
    return count

count = prinre(10001)
print(count)

输出:
...(无数个hello world!!!)
hello world!!!
hello world!!!
hello world!!!
运行时间为: 0.05481886863708496
10000
  • 函数的__name__等属性需要与开始的设定保持一致,所以要把调用函数f的属性复制到返回的函数wrapper中去

@functools.wraps(func):当没有这句代码时,print(f.name)返回的函数将是wrapper

import functools
def a(text):
    def d(func):
        @functools.wraps(func)
        def wrapper(*args,**kw):
            print('call %s %s():' % (text, func.__name__))
            return func(*args,**kw)
        return wrapper
    return d

@a('excute')

def f():
    print('2020-11-30')
print(f.__name__)
f()
输出:
f
call excute f():
2020-11-30
  • 实例:求100以内的每个数的平方并且输出个数和运行所用的是时间
import time
import functools

def metric(fn):
    @functools.wraps(fn)
    def wrapper(*args):
        t1 = time.time()
        result = fn(*args)
        t2 = time.time()
        print('%s executed in %s ms' % (fn.__name__, t2-t1))
        print('总个数是:%s 个' %result)
    return wrapper

@metric
def fn(num):
    count = 0
    for i in range(1, num):
        print('%s 的平方是:%s '% (i,i*i))
        count = count +1
    return count

fn(10001)
输出:
1 的平方是:1 
2 的平方是:4 
3 的平方是:9 
4 的平方是:16 
5 的平方是:25 
6 的平方是:36 
7 的平方是:49 
8 的平方是:64 
9 的平方是:81 
10 的平方是:100 
11 的平方是:121 
...
9997 的平方是:99940009 
9998 的平方是:99960004 
9999 的平方是:99980001 
10000 的平方是:100000000 
fn executed in 0.05081439018249512 ms
总个数是:10000 个

4、偏函数

Python的functools提供偏函数(Partial function)这个功能,当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单

  • 偏函数通过设定参数的默认值,可以降低函数调用的难度

int()函数可以把字符串转换为整数,当仅传入字符串时,int()函数默认按十进制转换

print(int('12345'))
输出:
12345

int函数还可以提供base参数

print(int('12345',base=16))
或者
print(int('12345',16))
输出:
74565

为这种转换进制编写一个函数int2

def int2(x, base=2):
    return int(x,base)
print(int2('1010110101'))
  • functools.partial可以帮助我们创建一个偏函数,不需要我们自己定义int2(),可以利用functools创建函数int2
import functools
int2 = functools.partial(int,base=2)
print(int2('1010110101'))
输出:
693
  • 在创建偏函数时参数可以接受函数对象、*args和**kw,并且偏函数在调用时可以传入自己需要的关键字参数

**kw:
kw = { ‘base’: 2 }
int(‘10010’, **kw) `

import functools
int2 = functools.partial(int,base=2)
print(int2('1010110101',base=10))
输出:
1010110101
  • 偏函数实例:设计一个函数求a+b+c+d的值
def num(a, b, c, d=1):
    return a+b+c+d

import functools
int2 = functools.partial(num,c=5)
print(int2(1,2))
输出:
9