python中高阶函数和装饰器_python高阶函数函数和装饰器一

[toc]

python高阶函数和装饰器一

First Class Object;

1.函数在python中是一等公民;

2.函数也是对象,可调用的对象;

3.函数可以作为普通变量、参数、返回值等;

高阶函数

1.数据概念y=g(f(x));

2.在数学和计算机科学中,高阶函数应当是至少满足下面一个条件的函数;

接受一个多个函数作为参数;

输出一个函数;

1

2

3

4

5

6

7

8

9

10def counter(base):

def inc(step=1):

nonlocal base

base = base + step

return base

return inc

foo = counter(10)

foo1 = counter(10)

print(foo())

print(foo1())

自定义sort 函数排序问题

1.仿照内建函数sorted, 请自行实现一个sort函数(不使用内建函数),能够为列表元素排序;

思路

1.内建函数sorted函数是一个返回一个新的列表, 可以设置升序或降序,也可以设置一个 排序的函数;自定义的sort函数也要实现这个功能;

2.新建一个列表, 遍历原列表,和新列表的值依次比较决定如何插入新列表中;

思考

sorted 函数的实现原理,扩展到map、 filter函数的实现原理;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15def sort(iterable,key=lambda a,b:a

ret = []

for x in iterable:

for i,y in enumerate(ret):

flag = key(x, y) if reverse else key(y,x)

if flag:

ret.insert(i,x)

break

else:

ret.append(x)

return ret

lst = [1,2,3,11,5,6,4,8,9]

print(sort(lst,reverse=True))

# 输出

[1, 2, 3, 4, 5, 6, 8, 9, 11]

内建函数-高阶函数sorted(iterable[,key][,reverse]) 排序

filter(function, iterable) --> filter objcet 过滤数据

map(func, *iterables) --> map object 映射

sorted(iterable[, key][, reverse]) 排序

返回一个新的列表,对一个可迭代对象的所有元素排序,排序规则为key定义的函数,reverse表是否排序翻转

sorted(lst,key=lambda x:6-x) # 返回新列表

lst.sort(key=lambda x:6-x) # 就地修改

filter(function, iterable)

过滤可迭代对象的元素,返回一个迭代器;

function一个具有一个参数的函数,返回bool;

例如,过滤出数列中能被3整除的数字;

list(filter(lambda x: x%3 == 0,[1,9,55,150,-3,78,28,123]))

map(function, *iterables) --> map object

对多个可迭代对象的元素按照指定的函数进行映射,返回一个迭代器;

list(map(lambda x:2*x+1,range(5)))

dict(map(lambda x:(x%5,x), range(500)))

柯里化(Currying)柯里化

指的是将原来接受两个参数的函数变成新的一接受一个参数的过程。新的函数返回一个以原有第二个参数的函数;

z = f(x,y) 转换成z= f(x)(y)的形式

举例

将加法函数柯里化

1

2def add(x,y):

return x +y

1

2

3

4

5

6

7

8

9In [1]: def add(x):

...: def _add(y):

...: return x + y

...: return _add

In [2]: foo = add(4)

In [3]: print(foo(5))

9

In [4]: print(add(4)(5))

9

通过嵌套函数就可以把函数转换成柯里化函数

Python 装饰器需求

一个加法函数,想增强它的功能,能够输出被调用过以及调用的参数信息;

1

2In [5]: def add(x,y):

...: return x + y

增加信息输出功能

1

2

3In [6]: def add(x,y):

...: print("calladd, x+y") # 日志输出到控制台

...: return x+y

上面的加法函数是完成了需求,但是有以下的缺点

打印语句的耦合太高;

加法函数属于业务功能,而输出信息的功能,属于非业务功能代码,不该放在业务函数加法中;

1

2

3

4

5

6

7In [10]: def add(x,y):

...: print("call {},{}+{}".format(add.__name__,x,y))

...: return x + y

...: add(4,5)

...:

call add,4+5

Out[10]: 9

做到了业务功能分离,但是fn函数调用传参是个问题

1

2

3

4

5

6

7

8

9

10

11def add(x,y):

return x + y

def logger(fn):

print('begin')

x =fn(4,5)

print('end')

return x

print(logger(add))

begin

end

9

1

2

3

4

5

6

7

8

9def add2(x,y,z):

return x+y+z

def logger(fn,*args,**kwargs):

print('before')

ret = fn(*args,**kwargs)

print('after')

return ret

print(logger(add2,4,z=5,y=6))

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25def add1(x,y):

return x + y

def add2(x,y,z):

return x+y+z

def logger(fn,*args,**kwargs):

def __logger(*args,**kwargs):

print('before')

ret = fn(*args,**kwargs)

print('after')

return ret

return __logger

foo = logger(add1)

print(foo(40,10))

print(logger(add1)(100,500))

# 输出

before

after

50

before

after

600

改进: 引入装饰器

装饰器语法糖

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15def logger(fn):

def __logger(*args,**kwargs):

print('before')

ret = fn(*args,**kwargs)

print('after')

return ret

return __logger

@logger # 相当于 add1 = logger(add1)

def add1(x,y):

return x + y

print(add1(100,1000))

# 输出

before

after

1100

写个装饰器,它就会把下面的的函数名提取出来,传给这个名称;

装饰器(无参)

它是一个函数

函数作为它的形参

返回值也是一个函数@functionname方式,简化调用

装饰器和高阶函数

装饰器是高阶函数,但装饰器是对传函数的功能的装饰(功能增强)

例子:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25import datetime

import time

def logger(fn):

def wrap(*args,**kwargs):

print("*args={},kwargs={}".format(args,kwargs))

start = datetime.datetime.now()

ret = fn(*args,**kwargs)

# after 功能增强

duration = datetime.datetime.now() - start

print("function {} took {}s.".format(fn.__name__,duration.total_seconds()))

return ret

return wrap

@logger # 相当于 add = logger(add)

def add(x,y):

print("====call add ========")

time.sleep(2)

return x + y

print(add(4,y=7))

# 输出结果

====call add ========

function add took 2.00511s.

11

装饰器函数

前置功能增强

被增强函数

后置功能增强

文档字符串Python 的文档

Python 是文档字符串的Documentation Strings;

在函数语句块的第一行,且习惯是多行的文本,所以多使用三引导;

惯例是首字母大写,第一行写概述,空一行,第三行写详细描述;

可以使用特殊属性doc访问这个文档;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19def add1(x,y):

'''This is a function

return int

x int

y int

'''

ret = x + y

return ret

add1(4,1000)

print(add1.__name__,add1.__doc__,sep='\n')

# 输出

add1

This is a function

return int

x int

y int

注: print(help(add1)) 函数本身就是调的__doc__

1

2

3

4

5

6

7Help on function add1 in module __main__:

add1(x, y)

This is a function

return int

x int

y int

None

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42#!/usr/bin/env python

def copy_properties(src,dst):

dst.__name__ = src.__name__

dst.__doc__ = src.__doc__

dst.__qualname__ = src.__qualname__

def logger(fn):

def wrapper(*args,**kwargs):

'''This is a wrapper'''

print('before')

ret = fn(*args,**kwargs)

print('after')

return ret

copy_properties(fn,wrapper)

return wrapper

@logger

def add(x,y):

'''

This is a function

return int

x int

y int

'''

ret = x + y

return ret

print(add.__name__,add.__doc__,add.__qualname__,sep='\n')

# 输出

add

This is a function

return int

x int

y int

add

装饰器副作用

原函数对象的属性都被替换了,而使用装饰器,我们的需求是查看被封装函数的属性;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30def copy_properties(src):

def _copy(dst):

dst.__name__ = src.__name__

dst.__doc__ = src.__name__

dst.__qualname__ = src.__qualname__

return dst

return _copy

def logger(fn):

@copy_properties(fn) # @ copy => wrapper = _copy(wrapper)

def wrapper(*args,**kwargs):

'''This is a wrapper'''

print('before')

ret = fn(*args,**kwargs)

print('after')

return ret

return wrapper

@logger

def add(x,y):

'''

This is a function

return int

x int

y int

'''

ret = x + y

return ret

print(add.__name__,add.__doc__,add.__qualname__,sep='\n')

# 输出

add add add

python 提供一个函数,被封装函数属性 == copy ==> 包装函数属性;

能过copy_properties函数将被包装函数的属性覆盖掉包装函数;

凡是被装饰的函数都城要复制这些属性,这个函数很通用;

可以将复制属性的函数构建成装饰器函数,带参装饰器;

带参装饰器1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26import datetime

import time

def logger(t):

def __logger(fn):

def wrap(*args,**kwargs):

start = datetime.datetime.now()

ret = fn(*args,**kwargs)

duration = (datetime.datetime.now() - start).total_seconds()

if duration > t:

print("function {} took {} s.".format(fn.__name__,duration))

return ret

return wrap

return __logger

@logger(3)

def add(x,y):

print("===call add===")

time.sleep(5)

return x + y

print(add(4, y=7))

# 输出

===call add===

function add took 5.001952 s.

11

带参装饰器

它是一个函数;

函数作为它的的形参;

返回值是一个不带参的装饰器函数;

使用@functionname(参数列表)方式调用;

可以看做在装饰器外层又加了一层函数;

将记录的功能提取出来,这样就可以能过外部提供的函数来灵我没有的控制输出

再次改造

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34import datetime

import time

def copy_properties(src):

def _copy(dst):

dst.__name__ = src.__name__

dst.__doc__ = src.__name__

dst.__qualname__ = src.__qualname__

return dst

return _copy

def logger(duration,func=lambda name,duration: print("{} took {}s".format(name,duration))):

def _logger(fn):

@copy_properties(fn)

def wrapper(*args,**kwargs):

start = datetime.datetime.now()

ret = fn(*args,**kwargs)

delta = (datetime.datetime.now() - start).total_seconds()

if delta > duration:

func(fn.__name__,duration)

return ret

return wrapper

return _logger

@logger(3)

def add(x,y):

print("===call add===")

time.sleep(5)

return x + y

print(add(4, y=7))

# 输出

===call add===

add took 3s

11

functools 模块functools.update_wrapper(wrapper,wrapped, assigned=WRAPPER_ASSGNMENTS, update=WRAPPER_UPDATES)

类似copy_properties 功能;

wrapper 包装函数,wrapped被包装函数;

元组WRAPPER_ASSIGNMENTS中是要被覆盖的属性;

1

2'__module__','__name__','__qualname__','__doc__','__annotations__'

模块名、名称、限定名、文档、参数注解

元组WRAPPER_UDPATES中是要被更新的属性, __dict__属性字典

增加一个__wapped__属性,保留着wrapped函数;

funtools.update_wrapper 实例

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42def copy_properties(src):

def _copy(dst):

dst.__name__ = src.__name__

dst.__doc__ = src.__name__

dst.__qualname__ = src.__qualname__

return dst

return _copy

import functools

def logger(fn):

def wrapper(*args,**kwargs):

'''This is a wrapper'''

print('before')

ret = fn(*args,**kwargs)

print('after')

return ret

functools.update_wrapper(wrapper,fn)

return wrapper

@logger

def add(x,y):

'''

This is a function

return int

x int

y int

'''

ret = x + y

return ret

print(add.__name__,add.__doc__,add.__qualname__,sep='\n')

print('#'*50)

print(add.__wrapped__)

## 输出

add

This is a function

return int

x int

y int

add

##################################################

格言

凡人做一事,便须全副精神注在些一事,首尾不懈,不可见异思迁,做这样想那样,坐之山望 那山,人而无恒,终身一事无成

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值