TOC
函数一等公民
在Python中,函数是一等公民(First-Class Object),以前在有的语言当中函数不是一等公民,也就是说函数定义好之后,函数的名称不能当变量用,函数就是函数,函数名一旦定义不能更改,函数也不可以作为实参,不能做为参数传递,所以函数不能像变量那么自由,对于这种我们就称之为不是一等公民;
但是现在在Python语言当中,函数名只是一个普通的标识符,只不过这个标识符与一个函数对象建立了关联关系,也就是说这个标识符是指向这个函数对象的;
在Python语言当中,我们可以看到可以将一个函数传递给另一个函数,如map、sorted函数,所以这个就已经实现了将一个函数作为参数传递给了函数,另外,在Python当中,函数也可以作为一个返回值,比如一个func函数,它都返回值就是一个lambda函数;
在Python中一切皆对象,函数也不例外,函数定义好了之后,一旦被解释器解释执行之后,它就会在内存中生成一个函数对象,所以函数也是对象,它是一个可调用对象, 所以说在Python中,函数既可以做实参传入,也可以做返回值返回,所以在Python中,函数就是一等公民;
高阶函数
高阶函数(Higt-order Function),在数学和计算机领域当中,高阶函数应当满足两个条件之一,第一,函数接收一个或多个函数作为参数(一般都可以协程lambda表达式),第二返回一个函数对象,满足两者之一,我们就称之为高阶函数,如下;
def counter(base):
def inc(step=1):
nonlocal base
base += step
return base
return inc
print(counter(1)()) # 2
内建高阶函数sorted
下面就利用高阶函数的特性,来自己实现实现sorted函数,实现排序的功能,并立即返回一个新的列表,大致分三步,第一步实现排序,第二步实现反转,第三步实现高阶函数,如下;
# 第一版实现排序
def sort(iterable, *, reverse=False):
newlist = []
for x in iterable:
for i, y in enumerate(newlist):
if x < y:
newlist.insert(i, x) # 找到合适的位置插入,并break
break
else:
newlist.append(x)
return newlist
lst = [1, 5, 2, 1, 4, 5, 7, 3]
print(sort(lst)) # [1, 1, 2, 3, 4, 5, 5, 7]
# 第二版,加入反转
def sort(iterable, *, reverse=False):
newlist = []
for x in iterable:
for i, y in enumerate(newlist):
order = x > y if reverse else x < y
if order:
newlist.insert(i, x) # 找到合适的位置插入,并break
break
else:
newlist.append(x)
return newlist
lst = [1, 5, 2, 1, 4, 5, 7, 3]
print(sort(lst,reverse=True)) # [7, 5, 5, 4, 3, 2, 1, 1]
# 第三版,实现高阶函数
def sort(iterable, *, key=None, reverse=False):
newlist = []
for x in iterable:
for i, y in enumerate(newlist):
rx = key(x) if key else x # 如果key存在,那么使用转换后的数据进行比较
ry = key(y) if key else y # 如果key存在,那么使用转换后的数据进行比较
order = rx > ry if reverse else rx < ry
if order:
newlist.insert(i, x) # key只用来比较,当插入时,还是插入原值
break
else:
newlist.append(x)
return newlist
lst = [1, 5, 2, "1", 4, 5, 7, 3]
print(sort(lst, key=str, reverse=False)) # [1, '1', 2, 3, 4, 5, 5, 7]
# 结合匿名函数
print(sort(lst, key=lambda x:int(x), reverse=True)) # [7, 5, 5, 4, 3, 2, 1, '1']
内建高阶函数map
map函数主要功能是,将一个容器当中的元素,依次按照key所指定的函数,转换成另一种形式,然后返回一个惰性求职对象,返回一个迭代器,说白了,就是从可迭代对象中,一个一个的取出迭代对象中的元素,然后对其,进行转换,最终将其返回,返回一个迭代器,所以map函数其实就是一个映射,将一个值映射成另一个值;
将一个可迭代对象的一个一个元素转换成另一种形式,这就是map要做的事情,在大数据领域广泛应用,海量数据,一亿一条,一万亿条都是用的map来实现的,但凡是是数据分析,只要他们所有的数据都有同一种格式其实都是使用map的思路来实现的,用一个统一的函数将你所需要的数据转换成另一种形式,然后进行reduce统计,这个map、reduce也就是hadoop的核心计算框架;
# 求平方
def square(x):
return x ** 2
lst = [1, 2, 3, 5]
print(list(map(square, lst))) # [1, 4, 9, 25]
# 匿名函数的实现
print({x: (x, x + 1) for x in map(lambda x: x + 1, range(1, 6))}) # {2: (2, 3), 3: (3, 4), 4: (4, 5), 5: (5, 6), 6: (6, 7)}
print(dict(map(lambda x: (x + 1, (x + 1, x + 2)), range(1, 6)))) # {2: (2, 3), 3: (3, 4), 4: (4, 5), 5: (5, 6), 6: (6, 7)}
# map也可以接收多个可迭代对象,但是结果以最短为准,此处需要知道一点,map的函数参数,如果返回一个值,map的最终结果会形成一个列表,返回两个值,则会形成一个列表包元祖,所以dict,可以将列表包元祖修改为字典;
print(dict(map(lambda x, y: (x, y), range(10), 'abcd'))) # {0: 'a', 1: 'b', 2: 'c', 3: 'd'}
l1=[1,2,3,4]
l2=[5,6,7,8]
def func(x,y): # 对于这个处理函数,即可以返回一个值,也可以返回两个值,当为一个值的时候,map的最终结果会形成一个列表,两个值时为一个列表元组;
return x*2,y*2
print(list(map(func,l1,l2))) # [(2, 10), (4, 12), (6, 14), (8, 16)]
内建高阶函数reduce
reduce,折叠,假设我们有一个序列0,1,2,3,4...,那么我们如果想求这序列中的所有元素的和,我们就可以使用reduce,并且,reduce还接受一个初始值;
reduce对于整个大数据计算来讲是非常重要的,reduce在迭代sequence(tuple ,list ,dictionary, string等可迭代物)的过程中,首先把 前两个元素传给 函数参数,函数加工后,然后把得到的结果和第三个元素作为两个参数传给函数参数, 函数加工后得到的结果又和第四个元素作为两个参数传给函数参数,依次类推。 如果传入了 initial 值, 那么首先传的就不是 sequence 的第一个和第二个元素,而是 initial值和 第一个元素。经过这样的累计计算之后合并序列到一个单一返回值;
from functools import reduce
print(reduce(lambda x, y: [x, y], [1, 2, 3, 4, 5])) # [[[[1, 2], 3], 4], 5]
# 可以通过这里看到,reduce是会对传入的函数进行循环传参,第一次传入的是序列的第一个和第二个元素,第二次像函数传入的第一个参数为第一次的运算结果,第二次的第二个参数则是序列中的第三个元素;
from functools import reduce
# 使用匿名函数
print(reduce(lambda x, y: x + y, [1, 2, 3, 4, 5])) # 15
# 自定义函数
l1=[1,2,3,4,5,6,7]
def f(x,y):
return x+y
print(reduce(f,l1)) # 28
# 设定初始值
l1=[1,2,3,4,5,6,7]
def f(x,y):
return x+y
print(reduce(f,l1,1)) # 29
内建高阶函数filter
filter函数,它本身是一个过滤器,它接受两个参数,一个是处理函数,如果处理函数为None,则原样输出,如果是一个函数则,按照函数进行过滤,第二个为处理的可迭代对象,filter函数主要为已知可迭代对象内的每个元素调用给定的Bool函数,返回值为True的元素,将会添加至一个新列表,返回值为False的将被过滤;
print(list(filter(lambda x: x > 10, range(6, 16)))) # [11, 12, 13, 14, 15]
# 定义布尔函数
def f1(n):
if n > 20:
return True
else:
return False
# 定义数据
lst=[1,2,3,6,71,21,53,12,3]
# 使用filter进行过滤
result=filter(f1,lst)
print(list(filter(f1,lst))) # [71, 21, 53]
函数注解
Python是动态语言,变量可以随时被赋值,也就是说Python的变量是运行时决定的,太过于灵活,越是灵活的工具,越需要驾驭者的水平,不然就会带来不好的副作用,Python也是如此,不到运行,无法判断类型是否正确,也就是一个函数,我们只有在执行的时候才知道传入的实参对不对,这出错就太晚了;
所以说动态语言,目前最大的问题是在于,它的这种没有类型化,导致一些与类型相关的错误,不能提前发现,我们不知道该用什么类型,我们也不知道返回值是什么类型,所以这就带来它的问题,所以在Python做小型项目的时候,似乎问题不大,但是如果用Python来做大型项目的时候,那就有问题了,埋藏着大量的隐患;
也正式因为这个诟病,问题会很多,Python社区里面就有很多人有这个呼声,说Python语言已经开发使用来写大型项目了,这个没有类型会带来很大的问题,经常在写项目的时候就因为类型的问题产生各种各样的BUG,那么Python也没有进行进一步的回应,在设计Python语言之初,就要求它不指定类型,如果指定类型那就成静态编译性语言了,那得从根上改变,所以Python就出了一种折中方案,折中折中方案就是类型注解annotation;
函数及变量注解
Python在3.5版本以后,支持两种注解,一种是函数注解,一种是变量注解,但是它只是一种声明,不是强制性的要求,不过呢,我们如果使用PyCharm开发,当我们不按规定传入指定类型参数时,可以看到颜色的变化,它可能不是红色,因为注解对于Python属于非强制性约束,只是友好提示,同时,使用__annotations__可以得到注解信息,如下;
# 函数注解
def add(x: int, y: int) -> list: # 限定x和类型和y的类型以及返回值是list类型
return x + y
print(add.__annotations__) # {'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'list'>}
print(add(1,2)) # 3
# 变量注解(version>3.6)
i:int=123
inspect类型检查
模块提供了一些有用的函数帮助获取对象的信息,例如模块、类、方法、函数、回溯、帧对象以及代码对象。例如它可以帮助你检查类的内容,获取某个方法的源代码,取得并格式化某个函数的参数列表,或者获取你需要显示的回溯的详细信息,该模块提供了4种主要的功能:类型检查、获取源代码、检查类与函数、检查解释器的调用堆栈;
import inspect
# 类型判断
print(inspect.isfunction(int)) # False
# 获取对象签名
print(inspect.signature(add)) # (x:int, y:int)