Python:函数式编程(map、reduce、filter、sorted、lambda、装饰器、偏函数)

函数式编程

允许把函数本身作为参数传入另一个函数,还允许返回一个函数
对于abs()这个函数,完全可以把函数名abs看成变量,它指向一个可以计算绝对值的函数

map函数:(作用于每一个元素,参数:函数,序列)

map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回

a = map(abs, [-10, -20, 30, -40])
print(list(a))

Iterator是惰性序列,因此通过list()函数让它把整个序列都计算出来并返回一个list

def func2(x):
    return x * x

b = map(func2, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print(list(b))

reduce函数:(累计计算,参数:函数,序列)

reduce把一个函数作用在一个序列[x1, x2, x3, …]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算

from functools import reduce

def func3(x, y):
    return x + y

print(reduce(func3, [1, 2, 3, 4, 5, 6, 7, 8]))  # 36

def func4(x, y):
    return x * 10 + y

print(reduce(func4, [1, 2, 3, 4, 5, 6, 7, 8])) 

把字符串转换为int

def func5(x, y):
    return x * 10 + y

def strtoint(s):
    digit = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
    return digit[s]

print(reduce(func5, map(strtoint, '56923')))

# 内置函数转换为int
print(type(int('99999')))

filter函数:(过滤序列,参数:函数,序列)

Python内建的filter()函数用于过滤序列。
和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。

def is_odd(x):
    return x % 2 != 1  # 偶数

def is_empty(x):
    return x and x.strip()

L = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
print(list(filter(is_odd, L)))
print(list(filter(is_empty, ['A', '', 'B', None, 'C', '  '])))

filter()函数返回的是一个Iterator,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list(和map一样)

sorted函数:(排序算法,参数:序列,key=函数)

Python内置的sorted()函数就可以对list进行排序

print(sorted([5, 2, 3, 4, 6]))  # [2, 3, 4, 5, 6]
print(sorted([-5, 2, 3, 4, -6]))  # [-6, -5, 2, 3, 4]
print(sorted([-5, 2, 3, 4, -6], key=abs))  # [2, 3, 4, -5, -6]
print(sorted(['bob', 'about', 'Zoo', 'Credit']))  # ['Credit', 'Zoo', 'about', 'bob']
print(sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower))  # ['about', 'bob', 'Credit', 'Zoo']
print(sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.upper))  # ['about', 'bob', 'Credit', 'Zoo']
print(sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.upper, reverse=True))  # 逆序排序 ['Zoo', 'Credit', 'bob', 'about']
def by_name(t):  # 按姓名排序
    return t[0]

def by_score(t):  # 按分数排序
    return -t[1]

L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
print(sorted(L, key=by_name))
print(sorted(L, key=by_score))

返回函数:

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

def lazy_sum(*x):
    def sum():
        retsum = 0
        for i in x:
            retsum = retsum + i
        return retsum
    return sum  # 返回sum函数

print(lazy_sum(1, 2, 3, 4, 5, 6, 7, 8))  # <function lazy_sum.<locals>.sum at 0x1058f9200>

# 因为lazy_sum返回的是一个函数,因此需要调用才可以打印出结果
print(lazy_sum(1, 2, 3, 4, 5, 6, 7, 8)())  # 36

返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量
传入到counter中的变量不能改变,我们只需要传一个不会变的值进去即可.而这个值不一定是常规的变量
比如下面的cnt设置成0的话,在counter函数中,我们并不能改变cnt的值,但是如果我们传的cnt是一个列表的话,其实本质上传递的是一个地址,在这个地址上我们可以做任意改变.只要这个列表还是这个列表,他的地址就不会变.利用这种特性,我们就可以随便折腾了

匿名函数:(lambda)

有些时候,不需要显式地定义函数,直接传入匿名函数更方便
在Python中,对匿名函数提供了有限支持。还是以map()函数为例,计算f(x)=x2时,除了定义一个f(x)的函数外,还可以直接传入匿名函数lambda x: x * x

print(list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8])))  # 求x的平方
print(list(filter(lambda x: x % 2 == 1, range(1, 20))))  # 筛选奇数

关键字lambda表示匿名函数,冒号前面的x表示函数参数

装饰器:

函数对象有一个__ name __ 属性,可以拿到函数的名字now.__ name __ = ’now’
假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)

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

# 把@log放到now()函数的定义处,相当于执行了语句now = log(now)
# wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用
# 在wrapper()函数内,首先打印日志,再紧接着调用原始函数
@log
def now():
    print('2020-6-28,12:00')
    
print(now.__name__)  # wrapper(name已经发生了改变了)
now()  # 2020-6-28,12:00
def log(test):
    def decorator(func):
        def wrapper(*args, **kw):
            print('%s %s():' % (test, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

@log('execute’)  # 可以输入指定的文本
def now():
    print('2020-6-28,12:00')

print(now.__name__)  # wrapper(name已经发生了改变了)
now()

因为返回的那个wrapper()函数名字就是’wrapper’,所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错
不需要编写wrapper.name = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的
定义wrapper()的前面加上@functools.wraps(func)即可

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

import functools
def log(test):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('call %s():' % func.__name__)
            return func(*args, **kw)
        return wrapper
    return decorator
# 设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间
def metric(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        t = time.time()  # 现在的时间
        func(*args, **kw)  # 运行一下函数
        print('%s executed in %s ms' % (func.__name__, time.time() - t))  # 计算时间差
        return func(*args, **kw)
    return wrapper

@metric
def fast(x, y):
    time.sleep(0.0012)
    return x + y

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

f = fast(11, 22)
s = slow(11, 22, 33)

偏函数:

通过设定参数的默认值,可以降低函数调用的难度。而偏函数也可以做到这一点。

print(int('10', 2))  # 计算10的二进制输出

def int2(x, base=2):  # 设计int2为专门输出二进制的函数
    return int(x, base)

print(int2('1000))  # 应用int2,输出8
functools.partial就是帮助我们创建一个偏函数的
int2 = functools.partial(int, base=2)
print(int2('1000))  # 8

创建偏函数时,实际上可以接收函数对象、*args和**kw这3个参数
functools.partial(int, base=2)实际上固定了int()函数的关键字参数base相当于:

kw = { 'base': 2 }
int('10010', **kw)

max2 = functools.partial(max, 10)实际上会把10作为*args的一部分自动加到左边,也就是

max2(5, 6, 7)就相当于:

args = (10, 5, 6, 7)
max(*args)  # 结果为10
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值