简要介绍python一等函数的基础知识以及函数式编程在python中的实现形式。



一等函数

首先要明确一等对象的概念,一等对象是一个编程语言里的概念,满足以下条件:

  1. 在运行时创建
  2. 能赋值给变量或者是数据结构中的元素
  3. 能作为参数传给函数
  4. 能作为函数的返回结果

在python中,常常把“函数视作一等对象”称为“一等函数”。

下例是把函数视为对象的一个例子:

def foo(n):
return n
tmp = foo
tmp(1)


高阶函数

接受函数为参数或者把函数作为返回结果的就是高阶函数。sorted是一个例子:

def reverse(word):
return word[::-1]
sorted(tmp, key=reverse)


上例的意思是按照reverse后的字典序给tmp这个列表排序。

在python3中比较常见的高阶函数还有map, filter, reduce,但是在python3中一般存在更好的方式替换这三种函数。

# map
list(map(foo, range(6)))
[foo(n) for n in range(6)]
# filter
list(map(foo, filter(lambda n: n % 2, range(6))))
[foo(n) for n in range(6) if n % 2]


可见,map和filter的直接替代品是生成器表达式。而reduce在python3中不再是内置函数,而是被放到了functools中:

reduce的通用思想是:把某个操作作用到序列的元素上,把一系列的值聚合成一个值。​​reduce菜鸟教程​

类似的内置函数有sum,any,all

  1. sum 求一个序列的值的和
  2. any(iterable) 只要iterable中有一个True,返回True
  3. all(iterable) iterable中的值都是True时,返回True

匿名函数

lambda关键字在python表达式中创建匿名函数。lambda中的定义体不能赋值,也不能使用try,while等语句

python中除非要作为参数传给高阶函数,否则尽可能不使用lambda函数。

可调用对象

除了用户定义的函数,可调用运算符也可以应用到其他对象上。可以使用callable判断是否可以调用。

  1. 用户定义的函数:使用def或者lambda创建
  2. 内置函数:使用C语言实现的函数
  3. 内置方法:dict.get
  4. 方法:在类的定义体中定义的函数
  5. 类:调用一个类时__new__->init
  6. 类的实例:如果类定义了__call__方法,那么它的实例也可以被调用。

接下来说明如何把类的实例变成可调用的对象

用户定义的可调用类型

任何实现了__call__的python对象都可以被调用。

函数内省

dir()

列出常规对象没有而函数有的属性,计算两个属性集合的差集即可

从定位参数到仅限关键字参数

调用函数时使用*和**展开可迭代对象,映射到单个参数。

def tag(name, *content, cls=None, **attrs):
if cls is not None:
attrs['class'] = cls
if attrs:
attr_str = ''.join(" %s=%s"%(attr, value) for attr, value in sorted(attrs.items()))
else:
attr_str = ''

if content:
return '\n'.join("<%s%s>%s</%s>"%(name, attr_str, c, name) for c in content)
else:
return '<%s%s />'% (name, attr_str)


简单理解就是,有名的参数被attr捕获,没有名字的参数被content捕获。

获取参数相关的信息

获取函数的签名,可以使用inspect模块:

from inspect import signature
sig = signature(foo)


其中,__defaults__元组保存定位参数和关键字参数的默认值。code.co_varnames保存参数名称。这样获取参数信息的问题在于这两个元组中的顺序不定,所以使用signature,生成一个有序映射,把参数名称和参数默认值一一对应起来。

而且signature中有一个bind方法,可以在参数传入函数之前进行格式的检查,如果确实必要的参数,会抛出TypeError,提示缺少参数。

函数注解

先来看一个有注解的函数:

def clip(text: str, max_len: 'int > 0' = 80) -> str:
......


参数的注解放在默认值前,冒号之后,返回值的注解在右括号后加一个箭头和返回值类型。注解不会进行任何处理,只会存在__annotations__中。

注意!!不会进行任何处理的意思是python只会把它当做一堆字串保存起来,仅作为参考的metadata使用,不会做类型检查,不做强制验证,不做类型验证。目前,只有signature函数知道如何提取注解。

sig.parameters


parameters是一个字典,建立参数名与参数注解的映射关系。

如果忘记了,直接看dir即可。

实际上,functools是一个非常有用的包,我们会在之后对其进行更深入的学习~