python学习-4-函数式编程,高阶函数,匿名函数,装饰器

什么是函数式编程

  • 允许将函数本身作为参数传入另一个函数
  • 还允许返回一个函数

高阶函数

  • 函数本身可以赋值给变量
  • 函数名本身就是指向函数的变量
    • 可以将函数名指向其他对象
  • 若一个函数可以接受另一个函数作为参数,该函数称之为高阶函数
def high_func(x,y,f):
    return f(x) + f(y)

map & reduce

  • map(func,Iterable)
    • 将传入的函数一次作用到序列的每个元素上
    • 并将结果作为新的Iterator返回
    • Iterator是一个惰性序列哦, 可以使用list将其转换成Iterable, 从而消除惰性
  • eg: 将列表中所有元素转换成字符串
list(map(str,[1,2,3,4,5]))
  • reduce(f,[x1,x2,x3,x4]) = f(f(f(x1,x2),x3),x4)
    • 要求f必须接收两个参数
    • 会将前面的结果作为第一个参数, 和Iterable中的参数进行运算
    • 是一种递归的感觉?
  • eg : 序列累加
reduce(add,[1,2,3,4,5])
# practice
def normalize(name):
    return name[0].upper() + name[1:].lower()

filter

  • 就是一个过滤器,根据设定好的函数作为条件,然后对Iterable进行过滤,并返回一个Iterator
  • eg: 筛选素数
# 奇数生成器
def _odd_iter():
    n = 1
    while True:
        n = n + 2
        yield n
# 筛选条件
def _not_divisible(n):
    return lambda x: x% n > 0
# 生成素数
def primes():
    yield 2
    it = _odd_iter()
    while True:
        n = next(it)
        yield n
        it = filter(_not_divisible(n),it)
  • 感觉还是别试图去搞清楚它的具体细节,是先生成奇数再过滤还是怎样
# 筛选出回文数
def is_palindrome(num):
    s = str(num)
    i,j = 0,-1
    while i<=len(s)/2:
        if s[i]!=s[j]:
            return False
        i += 1
        j -= 1
    return True

print(list(filter(is_palindrome, range(1, 200))))
# 这里可以用s[::-1] 翻转字符串 这样更简便

排序

  • sorted()
  • 可以接收key函数来实现自定义的排序
    • key指定的函数作用到Iterable上,根据其返回结果来排序
  • eg: sorted([‘Bob’,‘zip’,‘take’,‘Set’],key=str.lower,reverse=True)
def by_name(t):
    return t[0].lower()

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,reverse=True)) # 按分数从大到小排序

返回函数

  • 将函数作为函数的返回值 返回
  • 好处1: 返回一个函数,可以先不计算,等到需要时再使用
    • 这样就很快
    • 很像一个游戏道具,放你背包里,需要的时候拿来用
  • 内部函数可以引用外部函数的参数与变量
  • 每次都会返回一个新的函数,即使传入相同的参数
    • 两次调用不影响
def createCounter():
    x = 0
    def counter():
        nonlocal x # 有此声明,解释器会将x视作是外部的变量,已被初始化
        x += 1
        return x
    return counter

countA = createCounter()
print(countA())
print(countA())
print(countA())
  • 在需要对当前函数外部变量读取时: 直接读取,没问题
  • 在需要对当前函数外部变量修改时: 需要先声明nonlocal,才能使用

匿名函数

  • lambda表示匿名函数
  • 感觉与java中的差不多
  • lambda x: x * x
    • 等价于def f(x): return x*x
    • 第一个是参数,后面是返回值
  • 限制在于 只能有一个表达式
  • 好处
    • 匿名,不用担心冲突
    • 方便
print(list(filter(lambda x:x%2==1,range(1,20))))

装饰器

  • 定义
    • 希望 增强 函数的 功能,但不希望修改函数的定义
    • 代码运行期间动态增加功能
  • 函数.__name__可以获取函数名称
  • 定义方式:
    • 将 需要增强的 函数 当做参数传入
    • 定义一个新的子函数,其中增强,并返回原始函数
    • 外层函数返回子函数
    • 两层的目的为,可以在需要时才进行增强
def log(func):
    def wrapper(*args,**kw):
        print('call %s():' % func.__name__)
        return func(*args,**kw) # 这里直接运行结果
    return wrapper # 返回函数
  • 使用: 在需要增强的函数上打上注解@函数名
@log
def now():
    print(time.time())
  • 此时再使用now()时,解释器会自动根据注解,找到log方法返回的函数,去执行它,也就是wrapper()
    • now = log(now)
    • now() == log(now)() == wrapper()
    • 先找到log,传入now,得到wrapper
    • 之后运行wrapper
  • 如果需要给装饰器参数,就需要三层嵌套, 使用方法如下,注意参数的位置
def log(text):
    def decorator(func):
        print("test") # 代码一开始运行就打印,与函数执行无关
        def wrapper(*args, **kw):
            print(text) # 函数执行时被打印
            print('call %s():' % func.__name__)
            return func(*args, **kw)  # 这里直接运行结果
        return wrapper
    return decorator # 返回函数

@log("daf")
def now():
    print("当前时间:",time.time())
  • now = log(‘text’)(now)
    • 先找到log(‘text’),得到decorator
    • 然后decorator(now),得到wrapper
    • 之后运行wrapper
  • 最终 now.name == ‘wrapper’
def time_log(func):
    def wrapper(*args,**kwargs):
        start_time = time.time()
        res = func(*args,**kwargs) # 本身返回的就是一个元组,所以是没有问题的
        end_time = time.time()
        print("耗时: ",end_time-start_time)
        return res
    return wrapper

偏函数

  • 对于一些函数,我们需要大量使用,而其中一些参数不变
  • 那么我们就可以把这些参数 固定,生成一个新的函数,从而减轻开发压力
# 对于int(x,base=2) 二进制int
import functools
int2 = functools.partial(int,base=2)
# int2(xxx) == int(xxx,base=2)
# 默认与某个数比较大小
min0 = functools.partial(min,0)

l = [1,254,54,2]
print(min(l))
print(min0(*l))