路漫漫其修远兮
吾将上下而求索

python学习:高阶函数,匿名函数,返回函数=闭包,装饰器

变量f现在已经指向了abs函数本身。直接调用abs()函数和调用变量f()完全相同
f = abs
f(-10)
10

函数名其实就是指向函数的变量!对于abs()这个函数,完全可以把函数名abs看成变量,它指向一个可以计算绝对值的函数


既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
def add(x, y, f):
    return f(x) + f(y)
当我们调用add(-5, 6, abs)时,参数x,y和f分别接收-5,6和abs,根据函数定义,我们可以推导计算过程为:
def add(x, y, f):
    return f(x) + f(y)

x, y = -5, 6

sum = add(x, y, abs)
print(sum)


map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。
比如我们有一个函数f(x)=x2,要把这个函数作用在一个list [1, 2, 3, 4, 5, 6, 7, 8, 9]上,就可以用map()实现如下
def f(x):
    return x*x

r = map(f, list(range(10)))
print(list(r))

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]


reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,
如果要把序列[1, 3, 5, 7, 9]变换成整数13579,reduce就可以派上用场
from functools import reduce
def fn(x, y):
     return x * 10 + y

reduce(fn, [1, 3, 5, 7, 9])
13579


str2int的函数就是:
from functools import reduce
DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}

def str2int(s):
    def fn(x, y):
        return x * 10 + y
    def char2num(s):
        return DIGITS[s]
    return reduce(fn, map(char2num, s))


Python内建的filter()函数用于过滤序列。
filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
在一个list中,删掉偶数,只保留奇数
def is_odd(n):
    return n % 2 == 1

list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]


生成器示例,这里生成器可以直接作为filter的参数,可以理解为就是list
def _odd_iter():
    n = 1
    while n<100:
        n = n + 2
        yield n

def _not_divisible(n):
    return lambda x: x % n > 0

it = _odd_iter()

n =2

a = filter(_not_divisible(n), [2,3,4,5,6,7,8,9]) # 构造新序列
print(list(a))

b = filter(_not_divisible(n), it) # 构造新序列
print(list(b))


我们给sorted传入key函数,即可实现忽略大小写的排序:
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
['about', 'bob', 'Credit', 'Zoo']
要进行反向排序,不必改动key函数,可以传入第三个参数reverse=True:

>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
['Zoo', 'Credit', 'bob', 'about']
从上述例子可以看出,高阶函数的抽象能力是非常强大的,而且,核心代码可以保持得非常简洁。


闭包

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum
f = lazy_sum(1, 3, 5, 7, 9)
f()

在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,
相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@2
返回的函数并没有立刻执行,而是直到调用了f()才执行
def count():
    fs = []
    for i in range(1, 4):
        def f():
            return i*i
        fs.append(f)
    return fs

f1, f2, f3 = count()

print(f1())
print(f2())
print(f3())
全部都是9!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。


匿名函数
关键字lambda表示匿名函数,冒号前面的x表示函数参数
匿名函数lambda x: x * x实际上就是:
def f(x):
    return x * x

也可以起个名字
 f = lambda x: x * x
>>> f
<function <lambda> at 0x101c6ef28>
>>> f(5)
25


装饰器 
函数对象有一个__name__属性,可以拿到函数的名字

如果要在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下:
def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper

观察上面的log,因为它是一个decorator,所以接受一个函数作为参数,并返回一个函数。我们要借助Python的@语法,把decorator置于函数的定义处:
@log
def now():
    print('2015-3-25')

调用now()函数,不仅会运行now()函数本身,还会在运行now()函数前打印一行日志:
>>> now()
call now():
2015-3-25

把@log放到now()函数的定义处,相当于执行了语句:
now = log(now)



如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂
def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator
    
这个3层嵌套的decorator用法如下:
@log('execute')
def now():
    print('2015-3-25')
    
执行结果如下:
>>> now()
execute now():
2015-3-25

上面的原理是这样的:
>>> now = log('execute')(now)


设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间
import functools,time

def metric(fn):
    @functools.wraps(fn)
    def wrapper(*args, **kw):
        t0 = time.time()
        result = fn(*args, **kw)
        print('%s executed in %.4f ms' % (fn.__name__, time.time() - t0))
        return result
    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

print(fast(11, 22))
print(slow(11, 22, 33))
#fast executed in 0.0020 ms
#33
#slow executed in 0.1244 ms
#7986

示例中
def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper
返回函数,而上面的例子返回了一个值,该怎么理解!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!    
    

    

偏函数
简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。

def int2(x, base=2):
    return int(x, base)
这样,我们转换二进制就非常方便了:
>>> int2('1000000')
64

上面的方式可以简化
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85

未经允许不得转载:江哥架构师笔记 » python学习:高阶函数,匿名函数,返回函数=闭包,装饰器

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址