First Class Object

函数在Python中是一等公民,函数也是对象,可调用的对象,函数可以作为普通变量、参数、返回值等等

一、 高阶函数

1. 定义

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

1)接受一个或多个函数作为参数

2)输出一个函数

2. 例子

简单计数器:

def counter(base):
def inc(step=1):
nonlocal base #闭包
base += step
return base
return inc

函数counter是一个高阶函数,因为它输出了一个函数inc;

f1 = counter(5)和f2 = counter(5) 具有相等的函数返回值,f1 == f2为True

f1 = counter 和 f2 = counter返回不同的的函数对象

3. 内建函数 - 高阶函数:

3.1排序

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

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

3.2过滤数据

filter(function, iterable) --> filter object

功能:过滤可迭代对象的元素,返回一个迭代器,function一个具有一个参数的函数,返回bool。

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

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

3.3映射

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

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

例如:

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

4. 自定义sorted函数

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

思路:

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

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

#自定义sort函数 - 1
def my_sort(lst):
row = []
for x in lst:
for i,y in enumerate(row):
if x>y: #找到大的插入
row.insert(i,x)
break #不加break的话,会继续执行第二层for循环,随着元素的不断加入,一直会是2>1,第二层for循环会一直执行下去,直到撑爆内存
else:
row.append(x)
return row
print(my_sort([1,2,3,4,5]))
#自定义sort函数 - 2
def sort(iterable,reverse=False):
row = []
for x in iterable:
for i,y in enumerate(row):
flag = x>y if reverse else x
if flag:
row.insert(i,x)
break
else:
row.append(x)
return row
print(sort([4,2,5,1,3]))
#自定义sort函数 - 3
def sort(iterable,key=lambda a,b:a>b):
ret = []
for x in iterable:
for i,y in enumerate(ret):
if key(x,y): #函数的返回值是bool
ret.insert(i,x)
break
else:
ret.append(x)
return ret
print(sort([4,2,5,1,3]))
#自定义sort函数 - 4
def sort(iterable,reverse=False,key=lambda x,y:x>y):
ret = [] #未来排好序的列表
for x in iterable:
for i,y in enumerate(ret):
if key(x,y):
ret.insert(i,x)
break
else:
ret.append(x)
return ret
sort([4,2,5,1,3])
#自定义sort函数 - 4.1
def sort(iterable,reverse=False,key=lambda x,y:x>y):
ret = [] #未来排好序的列表
for x in iterable:
for i,y in enumerate(ret):
flag = key(x,y) if not reverse else not key(x,y)
if flag:
ret.insert(i,x)
break
else:
ret.append(x)
return ret
sort([4,2,5,1,3])

5. 自定义filter函数

filter函数源码:

class filter(object):
"""
filter(function or None, iterable) --> filter object
Return an iterator yielding those items of iterable for which function(item)
is true. If function is None, return the items that are true.
"""
def __getattribute__(self, *args, **kwargs): # real signature unknown
""" Return getattr(self, name). """
pass
def __init__(self, function_or_None, iterable): # real signature unknown; restored from __doc__
pass
def __iter__(self, *args, **kwargs): # real signature unknown
""" Implement iter(self). """
pass
@staticmethod # known case of __new__
def __new__(*args, **kwargs): # real signature unknown
""" Create and return a new object. See help(type) for accurate signature. """
pass
def __next__(self, *args, **kwargs): # real signature unknown
""" Implement next(self). """
pass
def __reduce__(self, *args, **kwargs): # real signature unknown
""" Return state information for pickling. """
pass

二、装饰器

1.柯里化

1,定义:指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数为参数的函数,如:z = f(x, y) 转换成 z = f(x)(y)的形式

2,举例:

将加法函数柯里化

def add(x, y):
return x + y
转换如下
def add(x):
def _add(y):
return x+y
return _add
add(5)(6)

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

2.装饰器(无参)

1,装饰器本质上是一个python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象。装饰器经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景

2,形成过程

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

def add(x,y):
return x + y
#增加信息输出功能
def add(x,y):
print("call add,x + y") #日志输出到控制台
return x + y
add(4,5)
#输出结果
call add,x + y
Out[1]:
9

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

打印语句的耦合太高

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

所以进一步改进:

def 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

在先前的基础上做到了业务分离功能,但是fn函数调用传参是个问题

def add(x,y):
return x + y
def logger(fn,*args,**kwargs):
print('begin')
x = fn(*args,**kwargs)
print('end')
return x
print(logger(add,4,y=5))

解决了传参问题,进一步改变,柯里化+高阶函数:

def add(x,y):
return x + y
def logger(fn):
def wrapper(*args,**kwargs):
print('begin')
x = fn(*args,**kwargs)
print('end')
return x
return wrapper
print(logger(add)(4,y=5))
#或者换种写法:
add = logger(add)
print(add(x=4,y=5))
最后,引入装饰器语法糖
def logger(fn):
def wrapper(*args,**kwargs):
print('begin')
x = fn(*args,**kwargs)
print('end')
return x
return wrapper
#装饰器语法糖
@logger #add = logger(add)
def add(x,y):
return x + y
print(add(4,5))

@logger就是装饰器的语法

综上,我们可以得出:装饰器(无参)是一个函数,函数作为它的形参,返回值也是一个函数,可以使用@functionname方式,简化调用;装饰器是高阶函数,但装饰器是对传入函数的功能的装饰(功能增强)

3.文档字符串和带参装饰器

Python文档字符串Documentation Strings

在函数语句块的第一行,且习惯是多行的文本,所以多使用三引号;惯例是首字母大写,第一行写概述,空一行,第三行写详细描述;可以使用特殊属性doc访问这个文档

例如:

def add(x,y):
"""This is a function of addition"""
a = x + y
return x + y
print("name={}\ndoc={}".format(add.__name__,add.__doc__)) #注意是双下划线
print(help(add))
#输出结果
name=add
doc=This is a function of addition
Help on function add in module __main__:
add(x, y)
This is a function of addition
None

而如果我们要使用装饰器的话,会发现原函数对象的属性都被替换了,例如:

def logger(fn):
def wrapper(*args,**kwargs):
'I am wrapper'
print('begin')
x = fn(*args,**kwargs)
print('end')
return x
return wrapper
@logger #add = logger(add)
def add(x,y):
'''This is a function for add'''
return x + y
print("name={}, doc={}".format(add.__name__, add.__doc__))
#输出结果

name=wrapper, doc=I am wrapper #输出的属性是wrapper函数的不是我们想要的add函数的

但是我们的需求是查看被封装函数的属性,解决方法为:

def copy_properties(src, dst): # 可以改造成装饰器
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
def logger(fn):
def wrapper(*args,**kwargs):
'I am wrapper'
print('begin')
x = fn(*args,**kwargs)
print('end')
return x
copy_properties(fn, wrapper)
return wrapper
@logger #add = logger(add)
def add(x,y):
'''This is a function for add'''
return x + y
print("name={}, doc={}".format(add.__name__, add.__doc__))

方法就是通过copy_properties函数将被包装函数的属性覆盖掉包装函数,凡是被装饰的函数都需要复制这些属性,这个函数很通用,可以将复制属性的copy_properties函数构建成装饰器函数,即带参装饰器。

def copy_properties(src):
def _copy(dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
return dst
return _copy
def logger(fn):
@copy_properties(fn) #wrapper = copy_properties(fn)(wrapper)
def wrapper(*args,**kwargs):
'I am wrapper'
print('begin')
x = fn(*args,**kwargs)
print('end')
return x
#copy_properties(fn, wrapper)
return wrapper
@logger #add = logger(add)
def add(x,y):
'''This is a function for add'''
return x + y
print("name={}, doc={}".format(add.__name__, add.__doc__))

@copy_properties(fn)带参装饰器,它是一个函数,函数作为它的形参,返回值是一个不带参的装饰器函数,使用@functionname(参数列表)方式调用.可以看做在装饰器外层又加了一层函数