Python学习笔记五函数式编程

Posted 老梧桐

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python学习笔记五函数式编程相关的知识,希望对你有一定的参考价值。

参考教程:廖雪峰官网https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000

一、返回函数

高阶函数除了可以接受函数作为参数之外,还可以把函数作为返回值。

通常我们也可以通过以下方式求和:

def calc_sum(*args):
    sum=0
    for n in args:
        sum=sum+n
    return sum

但如果有一种情况 ,不需要立刻得出求和结果,而是在后续的代码中根据需要再计算,这种情况不返回求和的结果,而是返回求和的函数:

def lazy_sum(*args):
    def funcsum():
        sum=0
        for n in args:
            sum=sum+n
        return sum
    return funcsum

x=lazy_sum(1,3,5)
#返回值赋给x,x是一个代入(2,3,5)元组局部变量的函数funcsum()
#通过输出可以看出x是一个函数
print(x)
#需要调用x()时候才输出求和结果
print(x())

对于上例,调用lazy_sum()时候传入的参数(1,3,5)成为了新创建的funcsum()函数的内部变量,这样的程序结构也称为“闭包(Closure)”。

需要注意,即便传入同样的参数,返回的函数也是不同的:

x1=lazy_sum(1,3,5)
x2=lazy_sum(1,3,5)
print(x1==x2)  #False

需要注意的是,返回函数并不立即执行,直到需要调用它的时候才执行,根据执行的时候各个参数和变量的值输出结果,看下面的例子:

def count():
    fs = []
    for i in range(1, 4):
        def f():
             return i*i
        fs.append(f)
    return fs   #返回一个函数列表

f1, f2, f3 = count()
fx=count()
print(f1())
print(f2())
print(f3())
print(fx[0]())
print(fx[1]())
print(fx[2]())

以上输出全为9,是因为在生成(返回)f1,f2,f3和函数列表fx的时候,它们内部的变量是i,而在执行的时候i已经变成了3。所以在使用闭包时需要牢记:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

如果一定要使用循环,那只有再创建一个函数,把循环变量绑定到其参数上,这样的绑定在循环过程中就实现了,可以保证循环后也不变:

def mycount():
    def f(j):
        def g():
            return j*j
        return g
    fs=[]
    for i in range(1,4):
        #此时i在循环中已经把值传递给g函数的参数j了
        fs.append(f(i))
    return fs

myf=mycount()
print(myf[0]())
print(myf[1]())
print(myf[2]())

练习:

#利用闭包返回一个计数器函数,每次调用它返回递增整数
def createCounter():
    #注意这里使用的变量是一个列表,这是避免一个报错bug
    mycounter=[0]
    def counter():
        mycounter[0]=mycounter[0]+1
        return mycounter[0]
    return counter

‘‘‘
python3中也可以用nonlocal先声明mycounter
这样就可以避免报错
def createCounter():
    mycounter=0
    def counter():
        nonlocal mycounter
        mycounter=mycounter+1
        return mycounter
    return counter
‘‘‘

tester=createCounter()
while input()!=3:
    print(tester())
    
# 测试:
counterA = createCounter()
print(counterA(), counterA(), counterA(), counterA(), counterA()) # 1 2 3 4 5
counterB = createCounter()
if [counterB(), counterB(), counterB(), counterB()] == [1, 2, 3, 4]:
    print(测试通过!)
else:
    print(测试失败!)   

 二、匿名函数

在Python中有时候不需要显示定义函数,这样也可以避免给函数起名的烦恼。

#关键字lambda就表示匿名函数,冒号前面的x是函数的参数
#返回值是x*x
f=lambda x:x*x
f(5)  #返回25

上面代码中的lambda表达式等同于下面代码:

def f(x):
    return x*x

一个lambda表达式在map()中的应用:

la=list(map(lambda x:x**2+1,[1,2,3,4,5]))
print(la)
#输出[2,5,10,17,26]

练习:

#请用匿名函数改造下面的代码:
def is_odd(n):
    return n % 2 == 1

L = list(filter(is_odd, range(1, 20)))

L1=list(filter(lambda x:x%2==1,range(1,20)))

print(L)
print(L1)

三、装饰器

某些情况下,我们需要增强若干个函数的功能,比如打印日志,但又不希望改动函数内部的代码,则有一种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)

例如,定义一个能打印日志的装饰器:

def log(func):
    def wrapper(*args,**kw):
        print(call %s % func.__name__)
        return func(*args,**kw)
    return wrapper

@log
def add(a,b):
    return a+b

print(add(3,6))

输出结果如下:

call add
9

注意到通过‘@‘符号在函数定义前增加了语句,即给该函数增加了一个装饰器功能。相当于执行了语句:now=log(add)。log(add)返回的是一个函数,该函数的功能不仅会运行函数本身,还运行了装饰器的代码。但这里now变量指向了新的函数,即wrapper。

如果要在装饰器中本身需要传入参数,则需要编写一个返回decorator的高阶函数:

def log(text):
    def decorator(func):
        def wrapper(*args,**kw):
            print(%s %s(): % (text,func.__name__))
            return func(*args,**kw)
        return wrapper
    return decorator

@log(haha)#注意这里参数的用法
def add(a,b):
    return a+b

print(add(3,9))
print(add.__name__)

我们可以发现,最后输出的函数名变成了装饰器内部返回的函数名了,为避免一些依赖函数名的代码执行错误,可以通过Python内置的functools.wraps:

import functools

def log(func):
    @functools.wraps(func)
    def wrapper(*args,**kw):
        print(call %s: % func.__name__)
        return func(*args,**kw)
    return wrapper

@log
def add(a,b):
    return a+b

print(add(3,9))
print(add.__name__)

练习:

#请设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间:
#-*- coding: utf-8 -*-
import time, functools

def metric(fn):
    @functools.wraps(fn)  #保持原始函数名
    def timedeco(*args,**kws):
        start_time=time.time()
        print(START TIME:+time.asctime(time.localtime(start_time)))
        x=fn(*args,**kws)
        end_time=time.time()
        print(END TIME:+time.asctime(time.localtime(end_time)))
        print(%s executed in %s ms % (fn.__name__, str(end_time-start_time)))
        return x
    return timedeco
        
# 测试
@metric
def fast(x, y):
    time.sleep(1.0012)
    return x + y;

@metric
def slow(x, y, z):
    time.sleep(1.1234)
    return x * y * z;

f = fast(11, 22)
print(f)
print(\n\n\n)
s = slow(11, 22, 33)
print(s)

if f != 33:
    print(测试失败!)
elif s != 7986:
    print(测试失败!)       
    

 

以上是关于Python学习笔记五函数式编程的主要内容,如果未能解决你的问题,请参考以下文章

python学习之路 五:函数式编程

vue.js学习笔记五

vue.js学习笔记五

vue.js学习笔记五

python学习笔记011——函数式编程

python学习笔记---函数式编程