zoukankan      html  css  js  c++  java
  • 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('
    
    
    ')
    s = slow(11, 22, 33)
    print(s)
    
    if f != 33:
        print('测试失败!')
    elif s != 7986:
        print('测试失败!')       
        
  • 相关阅读:
    IOS开发环境
    IOS开发环境搭建
    Eclipse简明使用教程(java集成开发环境)
    分布式相关
    成为架构师之路认识分布式架构
    什么是分布式系统,如何学习分布式系统
    分布式定义
    VIM命令详解
    vim常用命令
    vi/vim 命令使用详解
  • 原文地址:https://www.cnblogs.com/tsembrace/p/8544104.html
Copyright © 2011-2022 走看看