返回函数、闭包

返回函数

1.高阶函数接受函数作为参数,把函数作为结果值返回。

2.高阶函数返回int、str、list、dict等,返回函数!返回函数可以把一些计算延迟执行

【例1】定义求和函数

def calc_sum(list1):
    return sum(list1)


sum1 = calc_sum([1, 2, 3, 4]) 
print(sum1)
#输出结果是10

但是,返回函数可以“延迟计算”:

def calc_sum(list1):
    def yc_sum():
        return sum(list1)
    return yc_sum  # 返回一个函数对象,没直接返回值。

sum1 = calc_sum([1, 2, 3, 4])
sum2 = calc_sum([1,2,3,4])
print('调用calc_sum()并没有计算出结果,而是返回函数,sum1结果为:', sum1)
print('对返回的函数进行调用时,sum1()结果为:', sum1())
print('sum2结果为:', sum2)
print('sum2()结果为:', sum2())

python进阶学习笔记:2 python函数式编程_高阶函数

【思】:当声明一变量sum2=calc_sum([1,2,3,4]),则sum1和sum2两个内存存储地址比较结果是?不同

【析】:每次调用返回的是函数对象,不是值,故sum1和sum2是不同对象存储地址;两个调用并不会相互影响;

sum1==sum2 结果值为False;sum1()==sum2()结果值为True

闭包

内外函数:如果一个函数内部定义了另一函数,外部称之为外函数,内部称之为内函数;

闭包:在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用,那么这就构成了一个闭包;

闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内函数中用到,则把临时变量绑定给内函数,然后自己再结束

假如要计算y=ax+b的值,有如下几种做法:

A.使用函数:

# 1. 使用函数
def test(a, b, x):
    print("1. 使用函数",a * x + b)


test(1, 2, 1)
test(1, 2, 10)
test(2, 4, 1)
test(2, 4, 10)

缺点:需多次传入a,b的值

python进阶学习笔记:2 python函数式编程_高阶函数_02

B.使用全部变量:

#2. 使用全局变量
a = 1
b = 2
def test(x):
    print("2. 使用全局变量",a * x + b)

test(1)
test(10)
a = 2
b = 4
test(1)
test(10)

缺点:需多次修改全局变量a,b的值,麻烦

python进阶学习笔记:2 python函数式编程_高阶函数_03

C.使用缺省参数:

#3. 使用缺省参数
def test(x, a=1, b=2):
    print("3. 使用缺省参数",a * x + b)
test(1)
test(10)
test(1, a=2, b=4)
test(10, a=2, b=4)

优点:相比第三种方式,a和b是默认参数的一部分,不是全局变量

缺点:如果计算多条y=ax+b的值,需要多次传入a,b的值麻烦

python进阶学习笔记:2 python函数式编程_Time_04

D.使用实例对象:

# 4. 使用实例对象
class Test(object):

    def __init__(self, a, b):
        self.a = a
        self.b = b


    def __call__(self, x):
        print(self.a * x + self.b)


t1_2 = Test(1, 2)
t1_2(1)
t1_2(10)
t2_4 = Test(2, 4)
t2_4(1)
t2_4(10)

缺点,为了计算y的值,需要保存a,b的值,用了实例对象,浪费资源

python进阶学习笔记:2 python函数式编程_返回函数_05

E.使用闭包:

# 5. 使用闭包
def test(a, b):
    def create_y(x):
        print(a * x + b)
    return create_y
    
t1_2 = test(1, 2)
t1_2(1)
t1_2(10)
t2_4 = test(2, 4)
t2_4(1)
t2_4(10)

函数create_y与变量a,b构成闭包。创建闭包时通过test参数a,b说明两个变量的取值,确定函数的最终形式(y = x + 2和y = 2x + 4)。我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此,我们可以看到,闭包也具有提高代码可复用性的作用。

python进阶学习笔记:2 python函数式编程_返回函数_06

F.修改闭包中的数据

# 6. 修改闭包的数据
x = 100
def test1():
    x = 101
    def test2():
        nonlocal x
        print("--1--x: %s" % x)
        x = 102
        print("--2--x: %s" % x)
    return test2

test1()()

python进阶学习笔记:2 python函数式编程_返回函数_07

像在函数中修改全局变量一样,在python3中修改闭包中的数据需要使用nonlocal,就像上面那么:nonlocal x

 global与nonlocal区别:https://www.cnblogs.com/friedCoder/p/12708998.html


装饰器

概念

     装饰器是一个函数,让其他函数在不做任何代码变动下增加额外功能,装饰器的返回值是一个函数对象(函数指针)。装饰器函数的外部函数传入我要装饰的函数名字,返回经过修饰后函数的名字;内层函数(闭包)负责修饰被修饰函数。

装饰器属性:

  • 实质:是一个函数
  • 参数:要装饰的函数名(非函数调用
  • 返回:装饰完的函数名(非函数调用
  • 作用:为已存在对象添加额外功能
  • 特点:不需对对象做任何代码上变动

装饰器经典应用场景,如:插入日志、性能测试、事务处理、权限校验等。归纳出:装饰器最作用就是对于已写好程序,可抽离出一些雷同代码组建多个特定功能的装饰器,

函数装饰器

# FileName:       DecoratorTest 
# Description:函数装饰器

#有一台打印机,存在很多方法,打印、复印、彩印、扫描、传真等;在以上方法操作之前都必须先自检
# (自检当前设备连接接口是否正常)
#自定义一个装饰器
def  selfCheck(func):
    #需要实现额外扩展功能的函数
    def function():
        print("自检")
        func()
        print("自动断开连接")
    #实际就调用第二层函数:必须返回的是函数的对象,不能够是函数的具体值,具体实现
    return function
#打印功能
@selfCheck
def  printer():
    print("打印功能")


printer()
#复印功能
@selfCheck
def  copy():
    print("复印功能")
copy()
#彩印
@selfCheck
def  colorPrint():
    print("彩印功能 ")
#扫描
colorPrint()

python进阶学习笔记:2 python函数式编程_Time_08


#有一个需求:写过函数(九九乘法表、奇数偶数求和函数、求质数、斐波拉契....),在以上函数中还要完成
#每个函数执行时间的统计
#声明一个装饰器
import time
def  timeDec(func):
    def count_Time(number1,number2):
        start_Time=time.time()
        func(number1,number2)
        end_Time=time.time()
        print("运行时间:%s"%(end_Time-start_Time))
    return count_Time
@timeDec
def add(number1,number2):
    print(number1+number2)


add(1,2)
# def  language(*args):
#     def  get(name):
#         print(name)
#         name("哈哈哈哈哈")
#         for i in args:
#             print(i)
#     return get
def  language(type):
    if type=="Chinese":
       print("使用中文问候")
    elif type=="English":
        print("please use english wenhou")
    elif type=="taiYu":
        print("shua wo de  ka")
    def execute_Method(methodName):
        def func(name):
            methodName(name)
        return func
    return execute_Method


#打招呼的
def  chinese(name):
    print("你好:%s"%name)


@language("English")
def english(name):
    print("Hello:%s"%name)
#调用
english("John")
@language("Chinese")
def taiYu(name):
    print("萨瓦迪卡:%s"%name)
taiYu("瓦提啦啦啦")
def  janPan(name):
    print("扣你起哇:%s"%name)


#使用装饰器,传入对应的语言(Chinese、Enligh、TaiYu、JanPan),并给出对应的语种问候语

python进阶学习笔记:2 python函数式编程_返回函数_09

类装饰器  

# FileName:       Decorator_class
# Description:类装饰器
class Decorator(object):
    def __init__(self, f):
        self.f = f
    def __call__(self):
        print("decorator start")
        self.f()
        print("decorator end")

@Decorator
def func():
    print("func")

func()

python进阶学习笔记:2 python函数式编程_高阶函数_10

【注】__call__()是一个特殊方法,它可将一个类实例变成一个可调用对象,要使用类装饰器必须实现类中__call__(),就相当于将实例变成了一个方法。

装饰器链

      一个函数可被多个装饰器修饰,是从近到远依次执行。

# FileName:       Decorator_chain
# Description:装饰器链
def test_A(f):
    return lambda: "打印A" + f()


def test_B(f):
    return lambda: "打印B" + f()


@test_A
@test_B
def test_main():
    return "打印main"


print(test_main())

python进阶学习笔记:2 python函数式编程_Time_11

偏函数

偏函数 partial function,其实是函数的辅佐。

partial 一共有三个部分

(1)一个函数

(2)一个可变参数,*args

(3)一个关键字参

     int()函数可以把字符串转换为整数,当仅传入字符串时,int()函数默认按十进制转换,如果传入base参数,就可以做 N 进制的转换:

print(int('12345', base=8))
print(int('12345', 16))

python进阶学习笔记:2 python函数式编程_Time_12

        假设要转换大量的二进制字符串,每次都传入int(x, base=2)非常麻烦,于是,我们想到,可以自定义一个int2()的函数,默认把base=2传进去:

def int2(x, base=2):
    return int(x, base)

        但是有更简单的方法吗?functools.partial就是帮助我们创建一个偏函数的,不需要我们自己定义int2(),可以直接使用下面的代码创建一个新的函数int2:

import functools
int2 = functools.partial(int, base=2)
print(int2('1000000'))
print(int2('1010101'))

python进阶学习笔记:2 python函数式编程_返回函数_13

       functools.partial可以把一个参数多的函数变成一个参数少的新函数,少的参数需要在创建时指定默认值。


高阶函数(一)

   高阶函数:一个函数可以作为参数传给另外一个函数,或者一个函数的返回值为另外一个函数(若返回值为该函数本身,则为递归),满足其一则为高阶函数。

A.变量指向函数:函数本身也可以赋值给变量,即:变量可以指向函数

【例】求和函数sum

#A.变量指向函数:函数本身也可以赋值给变量,即:变量可以指向函数
print(sum([11,22]))

#如果我将函数结果赋值变量
sum1=sum([11,22])
print(sum1)

#得到结果也是33,如果此时直接输出sum呢
print(sum)

#说明此时获取的是函数对象,那么此时如果直接将该对象赋值给变量
sum2=sum
print(sum2)
#此时会发现变量sum2已经指向了sum函数对象本身了,也就是说明sum2变量同样可以实现求和操作
print(sum2([11,22]))

python进阶学习笔记:2 python函数式编程_Time_14


B.函数名也可以是变量:函数名也可以当做变量进行再次赋值

【例】

#B.函数名也可以是变量:函数名也可以当做变量进行再次赋值
print(sum([10,20]))
#如果将sum函数名进行作为变量重新赋值会出现什么情况呢?
sum=20
#如果此时继续调用sum进行求和会出现什么情况?
sum([1,2])
print(sum)

python进阶学习笔记:2 python函数式编程_Time_15

结论:把sum指向20后,就无法通过sum([1,2])调用该函数了!因为sum这个变量已经不指向求和函数了!

C.1.高阶函数:参数为函数

#参数为函数
def test1():
    print("这是第一个函数")
def test2(func):
    func()
    print("这是第二个函数")


test2(test1)

python进阶学习笔记:2 python函数式编程_高阶函数_16

2.高阶函数:返回值为函数;

#返回值为函数
def test1():
    print("这是第一个函数")
def test2(func):
    print("这是第二个函数")
    return test1
res=test2(test1)
res()

python进阶学习笔记:2 python函数式编程_高阶函数_17

编写高阶函数的目的:就是让函数的参数能够接收别的函数。

高阶函数(二)

D.常用的高阶函数:map、filter、reduce、sorted

D1.Map函数

  • 参数:一个函数,一个序列
  • 功能:将序列中的值处理再依次返回至列表内。
  • 返回值:一个迭代器对象。    

Round1:当seq(序列)只有一个时,将函数func作用于这个seq的每个元素上,并得到一个新的seq。

【例】

>>def  number(x):
...      return x*x
...
>>map(number,[1,2,3,4,5,6])
[1,4,9,16,25,36]
#会发现结果就是上图
#其次发现map第一个参数传入的是number函数对象,而不是调用number函数
#可以直接使用列表推导式或者循环解决不就行了
>>[number(i) for i in [1,2,3,4,5]]
[1,4,9,16,25,36]
#的确可以,当然用循环的话还需要声明空列表,然后追加,会显得更加复杂而不好理解;:

Round2:当seq多于一个时,map可以并行地对每个seq执行如下图所示的过程:

从图可以看出,每个seq的同一位置的元素同时传入一个多元的func函数之后,得到一个返回值,并将这个返回值存放在一个列表中。下面我们看一个有多个seq的例子

>>print map(lambda x , y : x ** y, [2,4,6],[3,2,1])
[8, 16, 6]
#大家可以思考:如果需要获取两个列表的每个元素的和以及每个元素的差进行构成一个元组,如果实现?

注意:map无法处理seq长度不一致、对应位置操作数类型不一致的情况,这两种情况都会报类型错误

Round3:使用map()函数可以实现将其他类型的数转换成list,但是这种转换也是有类型限制的,具体什么类型限制,在以后的学习中慢慢摸索吧。这里给出几个能转换的例子:


***将元组转换成list***
>>> map(int, (1,2,3))
[1, 2, 3]
***将字符串转换成list***
>>> map(int, '1234')
[1, 2, 3, 4]
***提取字典的key,并将结果存放在一个list中***
>>> map(int, {1:2,2:3,3:4})
[1, 2, 3]
***字符串转换成元组,并将结果以列表的形式返回***
>>> map(tuple, 'agdf')
[('a',), ('g',), ('d',), ('f',)]
#将小写转成大写
def u_to_l (s):
  return s.upper()
print map(u_to_l,'asdfd')

D2.Filter函数

         filter函数也是接收一个函数和一个序列的高阶函数,其主要功能是过滤。其返回值也是迭代器对象,例如:<filter object at 0x000002042D25EA90>,其图示如下:

语法:

filter(function, iterable)

实例:

# 过滤列表中所有奇数


def number(n):
    return n % 2 == 1


newlist = list(filter(number, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
print(newlist)
#结果输出:[1, 3, 5, 7, 9]

思考:过滤出1~100中平方根是整数的数;

D3.reduce函数

       reduce函数也是一个参数为函数,一个为可迭代对象的高阶函数,其返回值为一个值而不是迭代器对象,故其常用与叠加、叠乘等,图示例如下:

例如:

#reduce函数不是内置函数,而是在模块functools中的函数,故需要导入
from functools import reduce


nums=[1,2,3,4,5,6]
#reduce函数的机制
def reduce_test(func,array,ini=None): #ini作为基数
    if ini == None:
        ret =array.pop(0)
    else:
        ret=ini
    for i in array:
        ret=func(ret,i)
    return ret
#reduce_test函数,叠乘
print(reduce_test(lambda x,y:x*y,nums,100))
#reduce函数,叠乘
print(reduce(lambda x,y:x*y,nums,100))

D4.sorted函数

            Python内置函数,主要可以实现排序操作;说到排序,大家肯定会联想到列表中的sort方法;

列表有自己的sort方法,其对列表进行原址排序,既然是原址排序,那显然元组不可能拥有这种方法,因为元组是不可修改的。

而sorted函数则会返回副本;

注:默认sort和sorted方法由小到大进行排序排序,reverse=True时,由小到大进行排序

sort方法还有两个可选参数:key和reverse

1、key在使用时必须提供一个排序过程总调用的函数:

x = ['mmm', 'mm', 'mm', 'm' ]
x.sort(key = len)
print (x) # ['m', 'mm', 'mm', 'mmm']

2、reverse实现降序排序,需要提供一个布尔值:

y = [3, 2, 8 ,0 , 1]
y.sort(reverse = True)
print (y) #[8, 3, 2, 1, 0]

True为倒序排列,False为正序排列

具体两者区别实例:

>>>a = [5,2,1,9,6]        
     
    >>> sorted(a)                  #将a从小到大排序,不影响a本身结构 
    [1, 2, 5, 6, 9] 
     
    >>> sorted(a,reverse = True)   #将a从大到小排序,不影响a本身结构 
    [9, 6, 5, 2, 1] 
     
    >>> a.sort()                   #将a从小到大排序,影响a本身结构 
    >>> a 
    [1, 2, 5, 6, 9] 
     
    >>> a.sort(reverse = True)     #将a从大到小排序,影响a本身结构 
    >>> a 
    [9, 6, 5, 2, 1] 
     
   # 注意,a.sort() 已改变其结构,b = a.sort() 是错误的写法!


    >>> b = ['aa','BB','bb','zz','CC'] 
    >>> sorted(b) 
    ['BB', 'CC', 'aa', 'bb', 'zz']    #按列表中元素每个字母的ascii码从小到大排序,如果要从大到小,请用sorted(b,reverse=True)下同 
     
    >>> c =['CCC', 'bb', 'ffff', 'z']  
    >>> sorted(c,key=len)             #按列表的元素的长度排序 
    ['z', 'bb', 'CCC', 'ffff'] 
     
    >>> d =['CCC', 'bb', 'ffff', 'z'] 
    >>> sorted(d,key = str.lower )    #将列表中的每个元素变为小写,再按每个元素中的每个字母的ascii码从小到大排序 
    ['bb', 'CCC', 'ffff', 'z'] 
     
    >>> def lastchar(s): 
           return s[-1] 
    >>> e = ['abc','b','AAz','ef'] 
    >>> sorted(e,key = lastchar)      #自定义函数排序,lastchar为函数名,这个函数返回列表e中每个元素的最后一个字母 
    ['b', 'abc', 'ef', 'AAz']         #sorted(e,key=lastchar)作用就是 按列表e中每个元素的最后一个字母的ascii码从小到大排序 
     
    >>> f = [{'name':'abc','age':20},{'name':'def','age':30},{'name':'ghi','age':25}]     #列表中的元素为字典 
    >>> def age(s): 
           return s['age'] 
    >>> ff = sorted(f,key = age)      #自定义函数按列表f中字典的age从小到大排序  
     
    [{'age': 20, 'name': 'abc'}, {'age': 25, 'name': 'ghi'}, {'age': 30, 'name': 'def'}] 
     
    >>> f2 = sorted(f,key = lambda x:x['age'])    #如果觉得上面定义一个函数代码不美观,可以用lambda的形式来定义函数,效果同上