python语言基础(九)高阶函数、闭包、装饰器、推导式

本文详细介绍了Python中的高阶函数,包括map、filter、reduce的使用,并通过实例演示了它们的功能。接着讲解了闭包的概念,强调了其三个关键特征。然后讨论了装饰器的用途和实现,展示了如何利用装饰器计算函数运行时间并避免重复代码。此外,还解释了推导式,包括列表推导式、字典推导式和集合推导式,以及它们在简化代码中的应用。文章最后提供了一些练习题,帮助读者巩固所学知识。
摘要由CSDN通过智能技术生成

高阶函数

高阶函数需满足条件:

  • 函数名 作为 参数传入, 内置高阶函数map,filter,reduce
  • 函数名 作为 返回值 匿名函数作为返回值
  • 高阶函数是函数式编程的体现。函数式编程就是指这种高度抽象的编程范式。

高阶函数求绝对值

def sum_num(a, b, f):
    return f(a) + f(b)


result = sum_num(-4, 6, abs)
print(result)  # 10

内置高阶函数

map(function, iterable,…)

function:函数;iterable:一个或多个序列
map(),将传入的函数变量 作用到序列变量的每个元素中,并将结果组成新的迭代器返回,需要进行强转。

list1 = [1, 2, 3, 4, 5]
def func(x):
    return x ** 2


result = map(func, list1)

print(result)  # <map object at 0x000001E4B87C9828>
print(list(result))  # [1, 4, 9, 16, 25]

reduce(function, iterable[, initializer])

function – 函数,有两个参数;iterable – 可迭代对象;initializer – 可选,初始参数
reduce(func, lst),其中 func 必须有两个参数。每次 func 计算的结果继续和序列的下一个元素做累积计算。

import functools
lis1 = [1, 2, 3, 4, 5]
def func(a, b):
    return a + b


res = functools.reduce(func, lis1)  # 1和2相加的值,跟3继续相加,依次类推
print(res)  # 15

filter(function, iterable)

function – 判断函数;iterable – 可迭代对象。
函数用于过滤序列,过滤掉不符合条件的元素,返回一个 filter 对象,可以用list()转为列表。

lis1 = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
def func(x):
    return x % 2 == 0


res = filter(func, lis1)
print(res)  # <filter object at 0x00000253D7AC75C0>
print(list(res))  # [12, 14, 16, 18, 20]

闭包

闭包的定义需要满足以下三个条件:

  • 在一个外函数中定义了一个内函数
  • 内函数里运用了外函数的临时变量
  • 并且外函数的返回值是内函数的引用

在这里插入图片描述
代码从上到下执行,第一步在test(20)出处调用了test函数,打印了“–1--”,继续往下执行,test_in函数有被调用,到打印“–3--”,返回test_in到test的函数调用处,此时的res等同于test_in,res(25)相当于给test_in传递参数25,进入test_in函数的调用,打印“–2--”,返回 number_in + number到test_in的调用处,用res1接收,打印res1。

def test(number):  # number 形参 临时变量
    print("--1--")
    def test_in(number_in):
        print(number_in)
        print('--2--')
        return number_in + number
        pass
    print("--3--")
    return test_in
# test()执行完毕后,输出1,3。并且函数里面的变量(number)会被释放掉
res = test(20)  # res = test_in
res1 = res(25)  # test_in()
print(res1)  # 45,说明外层函数执行完毕后,number没有被释放掉,而是继续给内层函数使用
"""--1--,--3--,25,--2--,45。闭包是一种现象
总结:一般情况下,外层函数执行完毕之后,临时变量会被释放掉,但是此时
外层函数发现自己的临时变量会在将来被使用,在外层函数结束的时候,在返回内层函数的引用的同时,
将外层函数的临时变量与内层函数进行绑定"""

装饰器

例1.计算test1运行时间

import time
def test1():
    start_time = time.time()  # 当前时间
    print("--1--")
    time.sleep(2)  #  程序运行太快,相减会输出为 0,暂停2秒,再减掉
    end_time = time.time()  # 当前时间
    print(f"程序运行时间:{end_time - start_time -2}")

test1()

如果要测试test2,test3等等多个函数的时间,复制粘贴的话,会造成大量代码的冗余。可以把用于计算时间的函数单独封装

import time
def test_all(fun):
    start_time = time.time()
    fun()
    end_time = time.time()
    print(f"程序运行时间:{end_time - start_time - 2}")
def test1():
    print("--1--")
    time.sleep(2)
def test2():
    print("--2--")
    time.sleep(2)

test_all(test1)
test_all(test2)

将公共的代码提出来,封装成函数,然后以传参的时候传递函数。

装饰器

装饰器 (@) 是一种语法糖,主要用于在函数或类的基础上添加一些可重用的额外功能。从应用开发的角度来看的话,我们使用装饰器来对我们的应用代码进行功能性扩展和包装,以提高代码的可重用性和可维护性。不改变原有的代码的基础上,为函数添加新的功能。

由于我们在开发中要遵守封闭开放原则(实现的代码块尽量不做修改,可以拓展新的功能),所以python开发者开始使用装饰器,装饰器也就是说在不改变源代码的情况下为函数添加新的功能。并且使用@符号,@符号为语法糖。

应用场景:

在这里插入图片描述

# 需要在各个函数前增加打印“hello world”
def pri_hel(f):
    print('hello world!')
    return f

@pri_hel  # 装饰器,@+装饰器函数的名称。相应于test1的返回函数 test = f = test1
          # 相当于test1 =pri_hel(test),在下面函数没有调用的时候,以及提前执行了
def test():
    sum_li = sum([12, 3, 5])
    print(sum_li)
    return sum_li  # 可以省略
test1 =pri_hel(test)  # test函数作为实参传递给形参f,此时 test=f=test1,此行代码相当于@pri_hel
test1()  # f返回函数的调用处 test1,再调用test1,此时test1() = f() =  test(), 运行结果是  hello world 19

'''代码从上至下执行,到pri_hel(test)时,将test函数的引用(函数名)传入进去,test相当于f,打印hello world,把f返回到函数的调用处(test1),用test1来接收,test1就是f本身,test1()调用的是test函数,相当于在执行test函数前,执行了hello world'''

# 如果用test1 =pri_hel(test()),相当于f=test()=19,则说明在传参之前以及调用过依次test()。会先打印一次test()的结果19,得到的结果是 19 hello world 19

引入装饰器

def pri_hel(f):
    print('hello world!')
    return f

@pri_hel  # 装饰器,@+装饰器函数的名称。相应于 test = f 
         
def test():
    sum_li = sum([12, 3, 5])
    print(sum_li)
    return sum_li

test()

运行的结果是 :
hello world
19

def pri_hel(f):
    print('hello world!')
    return f

@pri_hel  # 相当于test =pri_hel(test),在下面函数test()没有调用的时候,装饰器就已经提前执行了
def test():
    sum_li = sum([12, 3, 5])
    print(sum_li)
    return sum_li

@pri_hel  # 相当于test2 =pri_hel(test)
def test2():
    print("我爱学习")  # 注意,test和test2函数在没有调用的时候,装饰器就已经执行,两个hello world会先被打印
test()
test2()

运行的结果:

hello world!
hello world!
20
我爱学习

如果把test(),test2()注释掉,也会直接打印两个hello world 。test()和test2()函数未被调用时,装饰器就已经开始执行了。注意装饰器与下面的函数在书写时不要留有空行。

例1的代码优化

# 有问题的代码,重复调用了test1()
import time
def test_all(func):    # func = test1
    start_time = time.time()
    func()   # 相当于test1()
    end_time = time.time()
    print(f"程序运行时间:{end_time - start_time - 2}")
    return func
def test1():
    print("--1--")
    time.sleep(2)

test =test_all(test1)
test()

**'也可以写为'**
import time
def test_all(func):    # func = test1
    start_time = time.time()
    func()   # 相当于test1()
    end_time = time.time()
    print(f"程序运行时间:{end_time - start_time - 2}")
    return func

@test_all
def test1():
    print("--1--")
    time.sleep(2)


test1()

返回的结果是

--1--
程序运行时间:0.0061604976654052734
--1--

代码从上至下执行,执行到test_all(test1),相当于把test1作为实参传入到 test_all(func),在函数内执行func(),调用了 test1,打印 ‘–1--’,然后计算结束时间,此时返回的func就是test1,返回到函数的调用处,此时 test = func = test1,最后用test()进行调用的时候,相当于 test1()执行了2次。

下面解决重复打印’–1--‘的问题

import time
def test_all(func):    # func = test1
    # 满足了闭包的条件
    def test_in():                  # 1.外函数里面有内函数
        start_time = time.time()
        func()   # 相当于test1()     # 2.内函数引用了外函数的临时变量
        end_time = time.time()
        print(f"程序运行时间:{end_time - start_time - 2}")
    return test_in                  # 3.内函数的引用作为外函数的返回值
def test1():
    print("--1--")
    time.sleep(2)

test = test_all(test1)     # test = test_in
test()   # 相当于调用了 test_in

返回的结果为

--1--
程序运行时间:0.0036284923553466797

程序从上至下执行,先执行 test_all(test1),将test1作为实参传递给func,test_all函数不会执行,返回 test_in 给函数的调用处 test,此时test = test_in,然后test() 相当于调用了 test_in(),然后去执行函数体内的内函数test_in(),此时func的函数名也是个变量名,func相当于临时变量,执行了test1函数的内容,然后执行print后,结束运行。

也可以写为

import time
def test_all(func):    # func = test1
    # 满足了闭包的条件
    def test_in():                  # 1.外函数里面有内函数
        start_time = time.time()
        func()   # 相当于test1()     # 2.内函数引用了外函数的临时变量
        end_time = time.time()
        print(f"程序运行时间:{end_time - start_time - 2}")
    return test_in                  # 3.内函数的引用作为外函数的返回值
@test_all
def test1():
    print("--1--")
    time.sleep(2)

test1()

返回的结果为

--1--
程序运行时间:0.000457763671875

在上面两个程序中,@test_all就相当于 test = test_all(test1) test()
运行过程中形成了闭包的现象,1.嵌套函数,2.内层函数引用外层函数的变量(函数名本质也是变量),3.内层函数的引用作为外层函数的返回值。

推导式

Python 中有一种特有的语法,又称为解析式。推导式是可以从一个数据序列构建另一个新的数据序列的结构体。
共有三种推导:列表(list)推导式;字典(dict)推导式;集合(set)推导式

列表推导式

例2.

生成一个[0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5,9.0, 9.5, 10.0] 的列表。

切记:所有的步长均不得为浮点数。不得写range(1,11,0.5),会报错

li = []
for i in range(1,21):
    # print(i/2)
    li.append(i/2)
print(li)

可以用列表推导式用简短的代码实现。

列表推导式1.

[out_express for out_express in input_list]
方括号里面for…in 的结构,for里面的表达式内容在in后的表达式里,for左边的是要添加到新的列表里的内容。

lis = [i/2 for i in range(1, 21)]
print(lis)

仅用2行代码实现刚才5行代码所显示的内容。

例3

有列表如:li = [6, 2, 6, 7, -15, 8, -17, -10, -15, -4]。需求:将li列表 <0 的数进行平方生成新列表。共三种方法

方法一:
li = [6, 2, 6, 7, -15, 8, -17, -10, -15, 4]
li1 = []
for i in li:
    if i < 0:
        i = i ** 2
        li1.append(i)
print(li1)
方法二:
# 将小于0的用filter过滤出来,然后用map映射做平方。
li = [6, 2, 6, 7, -15, 8, -17, -10, -15, 4]
lis = list(filter(lambda x:x<0,li))
print(list(map(lambda x:x**2,lis)))

列表推导式2

[out_express for out_express in input_list if out_express_condition]
for…in…if…结构,不是三目运算符([True_statements if expression else False_statement]),if后的条件如果满足,就执行for前面的语句,切记,千万不要加上else。

方法三:列表推到式
li = [6, 2, 6, 7, -15, 8, -17, -10, -15, 4]
lis1 = [i**2 for i in li if i < 0]
print(lis1)

例4

for i in '123':
    for j in 'abc':
        print(i+j)

可以用列表推导式来简化代码。

列表推导式3

推导式的嵌套
[i for row in matrix for i in row],可以往后一直嵌套

print([i+j for i in '123' for j in 'abc'])

字典推导式

{out_exp_key: out_exp_value for out_exp in input_list}

例5

实现以下列表中的元素与索引,形成{key:value}的形式

li = ['age', 'name', 'weight','gender']
print({i:li.index(i) for i in li})

集合推导式

{out_exp_res for out_exp in input_set}

例6

生成10个1-100之间的元素,并进行去重。

import random
print({random.randint(1, 100) for i in range(10)})

因为去重了,所以得到的值可能不会到10个。

注意:元组没有推导式,而是生成器。可以通过tuple()进行强转。

tu = (i for i in range(4))
print(tu)       # 生成器,<generator object <genexpr> at 0x0000026D7AD01C50>
print(type(tu)) # 生成器,<class 'generator'>
print(tuple(tu))    # (0, 1, 2, 3),强转为tuple类型

练习

1.过滤掉该列表names = [“jerry”,“hansen”,“Amy”,“Wendy”,“Tom”,“Bob”]把长度小于或等于3的字符串列表,并将剩下的转换成大写字母。

names = ["jerry", "hansen", "Amy", "Wendy", "Tom", "Bob"]
print([i.upper() for i in names if len(i) >3])

2.求(x,y),其中x是0-5之间的偶数,y是0-5之间的奇数组成的元组列表。
效果如下:[(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)]

第一种:
print([(x, y) for x in range(0, 5, 2) for y in range(1, 5, 2)])
第二种:
print([(x, y) for x in range(5) if x % 2 == 0 for y in range(5) if y % 2 == 1])

3.[ ‘Bob’,‘JOHN’,‘alice’,‘bob’,‘ALICE’,‘James’,‘Bob’,‘JAMES’,‘jAMeS’ ]
该列表很紊乱,实现去重,以及将名字格式统一成首字母大写(str.capitalize())

lis = [ 'Bob','JOHN','alice','bob','ALICE','James','Bob','JAMES','jAMeS' ]
print({i.capitalize() for i in lis})
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值