函数下(高阶函数+匿名函数+闭包+装饰器+命名空间)2020-11-18

1. 高阶函数

接受函数作为参数,或者将函数作为返回值返回的函数就是高阶函数。

1.1 案例:将指定列表中所有的偶数保存到新的列表

lst=[1,2,3,4,5,6,7,8,9,10]
def fn(l):        #   定义一个函数,将指定列表中的偶数保存到新的列表
    #参数l是要筛选的任意列表
    new_lst = []  # 创建一个空列表用来保存符合要求的元素
    for i in l:

        if i%2==0:  #判断寄偶
            new_lst.append(i)  #将符合要求的元素添加进新列表
    return new_lst   #返回新列表
print(fn(lst))

结果
在这里插入图片描述

1.2 案例:将指定列表中所有的奇数保存到新的列表

只需要将上述代码中判断条件改动一些即可:

lst=[1,2,3,4,5,6,7,8,9,10]
def fn(l):        #   定义一个函数,将指定列表中的奇数保存到新的列表
 #参数l是要筛选的任意列表
 new_lst = []  # 创建一个空列表用来保存符合要求的元素
 for i in l:

     if i%2!=0:  #判断寄偶,将条件改成恒不等于0
         new_lst.append(i)  #将符合要求的元素添加进新列表
 return new_lst   #返回新列表
print(fn(lst))

结果
在这里插入图片描述

1.3 案例:将指定列表中所有大于5的数保存到新的列表

将上述代码的判断条件再次改动一下:

lst=[1,2,3,4,5,6,7,8,9,10]
def fn(l):        #   定义一个函数,将指定列表中大于5的数保存到新的列表
    #参数l是要筛选的任意列表
    new_lst = []  # 创建一个空列表用来保存符合要求的元素
    for i in l:

        if i>5:  #判断是否大于5
            new_lst.append(i)  #将符合要求的元素添加进新列表
    return new_lst   #返回新列表
print(fn(lst))

结果
在这里插入图片描述

1.4 定义一个具有判断奇偶功能的函数

def fn1(i):
    if i%2==0:
        return True 	

将上述函数整体传递到下面的代码中:

def fn(l):
    new_lst = []  # 创建一个空列表用来保存符合要求的元素
    for i in l:
        if fn1(i):   此处判断条件直接用刚才定义的函数代替,如果判断结果返回真,下面的程序执行
            new_lst.append(i)  #将符合要求的元素添加进新列表
    return new_lst   #返回新列表
print(fn(lst))

结果
在这里插入图片描述

如果我又想要一个奇数列表怎么办呢?上述代码再改动一下

扫描二维码关注公众号,回复: 12233327 查看本文章
lst=[1,2,3,4,5,6,7,8,9,10]
def fn1(i):   #定义一个函数判断奇偶
    if i%2==0:
        return True
 # 将判断奇偶的函数当作判断条件,传递到下面的代码中
def fn(l):
    new_lst = []  # 创建一个空列表用来保存符合要求的元素
    for i in l:
        if not fn1(i):    此处判断条件也用刚才定义的函数代替,如果判断结果返回不是真,下面的程序执行
            new_lst.append(i)  #将符合要求的元素添加进新列表
    return new_lst   #返回新列表
print(fn(lst))

结果
在这里插入图片描述

1.5 定义一个判断是否大于5 的函数

如果我又想要一个大于5的数呢?
那么我同上面一样定义一个函数专门用来判断是否大于5

lst=[1,2,3,4,5,6,7,8,9,10]
def fn1(i):
    if i%2==0:
        return True
 # 定义一个专门用来判断是否大于5的函数
def fn2(i):
    if i>5:
        return True
 # 将判断奇偶的函数当作判断条件,传递到下面的代码中
def fn(l):
    new_lst = []  # 创建一个空列表用来保存符合要求的元素
    for i in l:
        if fn2(i):    #此处判断条件也用刚才定义的函数fn2代替,如果判断结果返回是真,下面的程序执行
            new_lst.append(i)  #将符合要求的元素添加进新列表
    return new_lst   #返回新列表
print(fn(lst))

结果
在这里插入图片描述

1.6 高阶函数概念引入

通过上面的案例我们可以看出,当判断条件后面的具有判断功能的函数变了

if (具有判断功能的函数):

那么整体的输出结果也就相应的改变。那么根据我们的要求,就可以将函数内的判断语句处传递一个相应的规则就可以了。
那么我们把上面的代码改动一如下:

lst=[1,2,3,4,5,6,7,8,9,10]
def fn1(i):
    if i%2==0:
        return True
 # 定义一个专门用来判断是否大于5的函数
def fn2(i):
    if i>5:
        return True
 # 将判断奇偶的函数当作判断条件,传递到下面的代码中
def fn(func,l): # 函数的形参改成两个,第一个是传递一个函数,表示这里的形参希望传入一个函数对象
    new_lst = []  # 创建一个空列表用来保存符合要求的元素
    for i in l:
        if func(i):    #此处判断条件用形参func替代
            new_lst.append(i)  #将符合要求的元素添加进新列表
    return new_lst   #返回新列表
print(fn(fn1,lst)) # 在调用函数的时候,要传入两个实参,第一个是我们想要的规则的函数,
 # 千万不要加括号,加括号等于调用函数,我们这里只是传参;第二个是一个待筛选的列表。

结果
在这里插入图片描述
假如我们想实现筛选大于5的数的功能,只需要将fn2传入即可
在这里插入图片描述
如果我们想要扩展其他的功能,我们只需要在函数外面定义该函数,然后使用的时候传入就行了。比如我们定义一个筛选3的倍数的函数。

# 定义一个判断3的倍数的函数
def fn3(i):
    if i%3==0:
        return True

整体代码如下:

lst=[1,2,3,4,5,6,7,8,9,10]
def fn1(i):
    if i%2==0:
        return True
def fn2(i):
    if i>5:
        return True
 # 定义一个判断3的倍数的函数
def fn3(i):
    if i%3==0:
        return True
def fn(func,l): 
    new_lst = []  
    for i in l:
        if func(i):   
            new_lst.append(i) 
    return new_lst  
print(fn(fn3,lst)) 

在这里插入图片描述
从以上案例我们可以看到,当我们传入的参数函数变了,相应的功能就改变了,这就是我们要讲的高阶函数
高阶函数有什么好处???
我们以前传参的时候都是传的,变量,数值,字符串,列表,都是单一的数据对象。但现在我们传递的是一个函数,里面包括很多的代码。
当我们把一个函数当作参数传递的时候,实际上是将指定的代码传递到目标函数。

2. 匿名函数

对于一些语法比较简单的函数,可以使用匿名函数创建,占内存更少,有利于做产品优化。

2.1 filter函数

filter函数的两个形参是:

  • function函数
  • interable可迭代对象(序列)
    我们通过一个实例来学习使用这个函数
lst=[1,2,3,4,5,6,7,8,9,10]
def fn4(i):
    if i%3==0:
        return True
    return False

print(filter(fn4,lst))


在这里插入图片描述
可以看到输出结果是一个filter类型的对象,后面是对象的内存地址。
如果我们要取出满足要求的序列,我们可以用list()函数转化一下

lst=[1,2,3,4,5,6,7,8,9,10]
def fn4(i):
    if i%3==0:
        return True
    return False

print(list(filter(fn4,lst)))

在这里插入图片描述
这里的结果就是列表了。这个函数也是把函数当作参数传递的,所以也是高阶函数

2.2 匿名函数lambda表达式

lambda表达式可以用来创建一些简单的函数,它是函数的另一种创建方式。
语法:lambda 参数列表 : 返回值
例如:我们定义一个函数,实现两个数的和,一般是这样做的:

def fn(a,b):
	return a+b

现在我们可以用lambda表达式这样做:

print(lambda a,b : a+b)

在这里插入图片描述

可以看到返回的是一个lambda函数对象 ,并没有实际结果。如果我们要求两个数的和,需要传入参数并调用。

print((lambda a,b : a+b)(100,200))

在这里插入图片描述
可以看到跟我们之前定义的函数输出结果是相同的。
也可以把lambda表达式这样使用:

fn6 = lambda a,b : a+b
print(fn6)
print(fn6(123,456))

在这里插入图片描述
lambda表达式的好处就是方便简单,用完之后就消失,不占内存。
适用于一些简单语法的函数,不适合复杂的语法规则。

3. 闭包

将函数作为返回值返回的也是高阶函数
例题:我们定义一个嵌套函数

def fn1():
    def fn2():
        print('我是fn2')
    return fn2
print(fn1())

在这里插入图片描述
可以看到返回的是一个函数对象。
再调用一次就会将内容打印出来。
在这里插入图片描述

也可以这样:

def fn1():
    def fn2():
        print('我是fn2')
    return fn2

r=fn1()
print(r)
r()

在这里插入图片描述
如果有多个函数,返回的是谁,调用的时候就会打印出谁。


def fn1():
    def fn2():
        print('我是fn2')
    # return fn2
    def fn3():
        print('我是fn3')
    return fn2
r=fn1()
r()

在这里插入图片描述
返回一下fn3

def fn1():
    def fn2():
        print('我是fn2')
    # return fn2
    def fn3():
        print('我是fn3')
    return fn3
r=fn1()
r()

在这里插入图片描述

3.1 回顾变量的作用域

def fn1():
    def fn2():
        a=10
        print('我是fn2',a)
    # return fn2
    def fn3():
        print('我是fn3')
    return fn2
r=fn1()
r()

这样可以把a值打印出来
在这里插入图片描述
在函数的外面是不能打印出来的

def fn1():
    def fn2():
        a=10
        print('我是fn2',a)
    # return fn2
    def fn3():
        print('我是fn3')
    return fn2
r=fn1()
# r()
print(a)

在这里插入图片描述
这就好像内部有一个包
在这里插入图片描述

  • 这种函数,以函数为返回值的函数,我们也称之为闭包
  • 通过闭包我们可以创建一些只有当前函数能够访问的变量,我们可以将一些私有的或者重要的数据藏在闭包中。
    例题:定义一个函数求列表内元素的平均值。
lst = [10,20,30,40,50]
def fn():
	return sum(lst)/len(lst)

在这里插入图片描述
现在我们优化一下,可以让这个函数求你t添加后的数的平均值。

lst=[]
def fn(n):
	lst.append(n)
	return sum(lst)/len(lst)
print(fn(10))
print(fn(20))

在这里插入图片描述
我们看到,每传递一次,列表都在改变。
在这里插入图片描述
也就是说,我们的每一次操作都会影响lst的值。能不能让后面的操作不影响这个变量呢?当然可以,这就是我们说的闭包的作用。
例题:

def make_fn():
    lst = []
    def fn3(n):
	    lst.append(n)
	    return sum(lst)/len(lst)
    return fn3
r=make_fn()

print(r(10))
lst = []
print(r(20))

在这里插入图片描述

闭包的形成条件:

  • 函数嵌套
  • 将内部函数作为返回值返回
  • 内部函数必须要使用到外部函数的变量

4. 装饰器

创建几个简单功能的函数:

def add(a,b):
	 #求任意两数的和
	 print('开始计算... ...')
	 r = a+b
	 print('结束计算... ...')
	 return r
def mul(a,b):
	#求任意两数的积
	return a*b
print(add(1,2))
print(mul(3,4))

结果
在这里插入图片描述
我们可以直接修改代码去修改函数的功能,但如果要修改的函数比较多,修改起来比较麻烦。维护起来也不方便。也违反开闭原则(ocp:该原则要求对开发程序的扩展,要求关闭程序的修改。意思就是说,你可以扩展程序的功能,但是不要修改我程序的源码)。
例题:我希望在不修改原函数的前提下来对函数进行扩展

def fn():
	print('我是函数fn... ...')
def fn2():
	print('函数开始了... ...')
	fn()
	print('函数结束了... ...')
fn2()

在这里插入图片描述
这样我们看到函数fn的功能扩展了,但是没有修改函数fn的代码。我们也可以用这个办法对其他函数进行扩展。
例题:

def add(a,b):
    r=a+b
    return r
def new_add(a,b):
    print('函数开始')
    r=add(a,b)
    print('函数结束')
    return r
r=new_add(123,456)
print(r)

结果
在这里插入图片描述

这里每扩展一个函数的功能要定义一个新的函数,也不方便。我们能不能定义一个函数,可以扩展所有函数的功能而不改变他们的源码呢?当然可以,这就是我们要讲的装饰器

4.1 装饰器的推理过程

例题:定义一个新的函数,用来对其他函数进行功能的扩展,扩展的功能是添加两个打印语句“函数开始执行”,“函数执行结束”。
第一步,创建一个函数:

def start_end():
	#里面再嵌套一个新函数
	def new_function():
		pass
	# 返回新的函数
	return new_function
#设置一个变量接受这个函数
f=start_end()
#打印一下返回的应该是一个函数对象
print(f)

结果
在这里插入图片描述
下面我们看看这个装饰器的代码如何写,根据以上经验,我们可以把要扩展的函数添加进来。

def fn():
	print('我是函数fn')
	
def start_end():
	#里面再嵌套一个新函数
	def new_function():
		print('函数开始了....')
		fn()
		print('函数结束了....')		
	# 返回新的函数
	return new_function
#设置一个变量接受这个函数
f=start_end()
#打印一下返回的应该是一个函数对象
#print(f)
f()
#我们先调用一下这个函数

结果
在这里插入图片描述
我们发现确实对函数fn进行扩展了,那对别的函数能扩展吗?比如对函数add()进行扩展。我们可以把函数的形参设置为old意思是旧的函数,里面涉及到的要扩展的函数都设置为old。

def fn():
	print('我是函数fn')
	
def start_end(old):
	#里面再嵌套一个新函数
	def new_function():
		print('函数开始了....')
		old()
		print('函数结束了....')		
	# 返回新的函数
	return new_function

现在我们先调用这个装饰器对函数fn()进行装饰。

#设置一个变量接受这个函数
f=start_end(fn)  #传入函数fn作为实参
f()  #调用f

结果:
在这里插入图片描述
下面我们再对函数add()进行装饰试试。

def add(a,b):
    r=a+b
    return r
def start_end(old):
	def new_function():
		print('函数开始了....')
		old()
		print('函数结束了....')		
	return new_function
f=start_end(add)
f()

结果
在这里插入图片描述

报错了,报错显示add()缺少两个必须的位置参数。那我们改动一下代码,加入两个位置参数:

def add(a,b):
    r=a+b
    return r
def start_end(old):
	def new_function(a,b):
		print('函数开始了....')
		result=old(a,b) #这里需要设置变量来接受结果用来返回
		print('函数结束了....')	
		return result	
	return new_function
f=start_end(add)
r= f(1,2)  #因为有返回值,这里也要设置返回值
print(r)

结果
在这里插入图片描述
确实已经扩展了函数add(),但是如果我们回头再用这个装饰器扩展fn()函数,又因为有参数而报错。那有没有一种方法可以接收很多参数,而且无论你的函数有没有参数都可以对其进行装饰呢?有的。是否还记得不定长参数*a和不定长关键字参数**a的知识呢?

请看如下代码:

def fn():
	print('我是函数fn')
def add(a,b):
    r=a+b
    return r
def start_end(old):
	def new_function(*a,**b):
		print('函数开始了....')
		result=old(*a,**b) #这样写就可以了
		print('函数结束了....')	
		return result	
	return new_function
f=start_end(add)
r=f(1,2)
print(r)

结果
在这里插入图片描述

再来装饰一下函数fn()

def fn():
	print('我是函数fn')
def add(a,b):
    r=a+b
    return r
def start_end(old):
	def new_function(*a,**b):
		print('函数开始了....')
		result=old(*a,**b) #这样写就可以了
		print('函数结束了....')	
		return result	
	return new_function
f=start_end(fn)
f()

结果
在这里插入图片描述

我们可以看到整个装饰器已经可以适应各种函数了。
类似start_end这一类的函数就叫装饰器,通过装饰器可以在不修改其代码的情况下对其他函数进行扩展。在开发中都是用装饰器进行功能扩展的。

4.2 装饰器的使用

但一般装饰器不是这样调用的。比如我有这么一个函数需要装饰:

def speak():
	print('同学们,大家晚上好!')

可以直接在紧挨着函数的上方写上

@start_end

来使用装饰器,我们试一下

@start_end
def speak():
	print('同学们,大家晚上好!')
speak()

结果
在这里插入图片描述
再试一下

@start_end
def fn():
	print('我是函数fn')
fn()

在这里插入图片描述

再来最后一次

@start_end
def add(a,b):
    r=a+b
    return r
r=add(1,2)
print(r)

结果
在这里插入图片描述
好的,装饰器知识到此就讲完了。当然,这是一个简单功能的装饰器,你用的时候可以自行扩展。

5. 命名空间

5.1 用locals()来获取当前作用域的命名空间

如果locals()在全局里使用获取的就是全局的命名空间,如果在函数内使用获取的就是函数的命名空间,命名空间返回的是一个字典。
例如:

a=10
def fn():
	print('你好')
scope=locals()
print(scope)

结果:

D:\Python38\python.exe D:/work/基础/Day11/Demo01.py
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000027AB53308B0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/work/基础/Day11/Demo01.py', '__cached__': None, 'fn6': <function <lambda> at 0x0000027AB547E160>, 'a': 10, 'fn': <function fn at 0x0000027AB547E1F0>, 'scope': {...}}

Process finished with exit code 0

这里返回的是一些特殊方法,或者称为“魔术方法”。
我们可以看出scope返回的是一个字典,我们可以通过字典的知识来打印一个a的值

print(scope['a'])
print(a)

在这里插入图片描述
可以看到这两种方式的返回值一模一样。我们再玩儿一个酷的,例如,整个代码中全程都没有定义c变量,但是我们可以用字典的知识去添加,然后再打印。理论上应该是报错c这个变量not defined,但是请看。

scope['c']=20
print(scope['c'])

结果是
在这里插入图片描述
我们没有定义c,只是在命名空间里添加了一个键值对。就相当于在全局中创建了一个变量。但python不支持这种方法,所以不建议你这么做。
如果我们在函数的内部使用locals()就获取函数的命名空间。

def fn():
	a=123
    scope=locals()
    print(scope)
fn()

在这里插入图片描述
那么在局部空间能不能这样玩儿呢?

def fn():
	a=123
    scope=locals()
    scope['c']=30
    print(scope)

在这里插入图片描述
这个要看python的版本,我的是3.8版本的,是可以的。
打印就不行了,不信你瞧。
在这里插入图片描述
报错了。所以你编程时千万不要这样玩儿。还有保存文件时一定要使用英文,最好不要出现中文。

6. 作业

6.1 写博客梳理知识点

6.2 课堂代码敲三遍起步

6.3 以前代码没有敲够的补上

猜你喜欢

转载自blog.csdn.net/m0_46738467/article/details/109767779