18Python函数式编程

Posted 日常随笔记

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了18Python函数式编程相关的知识,希望对你有一定的参考价值。

本章笔记主要包括:函数引用、高阶函数、内建函数、匿名函数、偏函数

1、函数引用

  我们通常定义变量的采用如下方式:

age = 10

这就表明了age这个变量具有了表示年龄的这种能力,Python中变量不仅仅可以接收基本类型和我们创建的对象,同样它也允许接收函数。当变量接收函数的时候,他就具备了函数的能力,我们来看一下代码:

def func():
   print("测试函数引用")


if __name__ == '__main__':
   #常规方式调用
   func()
   #赋值给变量
   funTest = func
   #查看类型
   print("func的类型 :", type(funTest))
   print("funTest的类型 :", type(funTest))
   #比较地址
   print(id(func))
   print(id(funTest))
   #调用
   funTest()

运行结果:

测试函数引用
func的类型 : <class 'function'>
funTest的类型 : <class 'function'>
31313024
31313024
测试函数引用

  从结果上我们可以看出,当把函数赋值给变量的时候,当前变量的类型是函数,变量获取的是函数的引用,和我们通常方式不同的是,当变量接收函数,使用形式是“变量名()”。

2、高阶函数

  之前我们见到的函数都是传入的形参普通的变量类型,返回的也只是普通的类型,代码如下:

def sum(a,b):
   return a + b

if __name__ == '__main__':
   num = sum(1, 2)
   print("求和结果是:%d" %num)

  我们上面谈到的函数引用,表明变量允许被赋值为函数,此处所谓的高阶函数其实就是一个函数允许接受一个函数作为参数或返回值是一个函数,形参为函数的代码如下:

def func(a, b):
   return a + b

def sum(a, b, f):
   return f(a,b)

if __name__ == '__main__':
   print("高阶函数求和结果:" , sum(1, 2, func))

  代码中不难看出sum函数自身是不具备计算的功能的,他之所以能计算是调用了我们传入的func函数。

  除了形参可以传入函数,Python中还允许函数作为返回值,代码如下:

def sum(a, b):
   def func():
       return a + b
   return func
if __name__ == '__main__':
   num = sum(1, 2)
   print("返回值类型是", type(sum))
   print("高阶函数求和结果:" , num())

运行结果:

返回值类型是 <class 'function'>
高阶函数求和结果: 3

  当在函数内部定义了一个函数,且使用到外部函数的变量的时候,我们成内部的这个函数叫“闭包”,闭包是一直持有外部变量状态的,我们来看个斐波那契的例子:

def count():
   #存放数列
   fib = []
   #a用来表示前一个值,b表示a后面的值
   #num记录当前是第几组数据
   a, b, num = 0 ,1, 1
   print("斐波那契数列数据\n")
   def totalFib():
       #如果要修改外部函数的局部变量使用nonlocal在内部函数声明
       nonlocal a , b, num
       print("第" + str(num) + "组:")
       fib.append(b)
       a, b, num = b , a + b, num + 1
       return fib
   return totalFib
if __name__ == '__main__':
   fib = count()
   for x in range(5):
       print(fib())

运行结果:

斐波那契数列数据
1组:
[1]
2组:
[1, 1]
3组:
[1, 1, 2]
4组:
[1, 1, 2, 3]
5组:
[1, 1, 2, 3, 5]

  通过运行的结果我们可以发现,内部函数是一直持有外部函数的值的。闭包可以提高代码的复用性。

注:

  • 闭包因为可以外部局部变量的持有状态,所以会导致占用内存

3、内建函数

  python解释器启动后会默认加载一些函数,这些函数称之为内建函数,我们并不需要刻意的去记忆这些内建函数,查看加载的函数和函数使用方式,可以使用如下代码:

#查看内联函数和属性
print(dir(__builtins__))
#查看内联函数使用方式
help(print)

运行结果:

#内联函数及属性
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
#print方法定义规则
Help on built-in function print in module builtins:
print(...)
   print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
   Prints the values to a stream, or to sys.stdout by default.
   Optional keyword arguments:
   file:  a file-like object (stream); defaults to the current sys.stdout.
   sep:   string inserted between values, default a space.
   end:   string appended after the last value, default a newline.
   flush: whether to forcibly flush the stream.
None

  下面我们来看看几个常用的内建函数。

3.1 map

  使用help查询map的使用方式:

Help on class map in module builtins:
class map(object)
|  map(func, *iterables) --> map object
|  
|  Make an iterator that computes the function using arguments from
|  each of the iterables.  Stops when the shortest iterable is exhausted.
|  
|  Methods defined here:
|  
|  __getattribute__(self, name, /)
|      Return getattr(self, name).
|  
|  __iter__(self, /)
|      Implement iter(self).
|  
|  __new__(*args, **kwargs) from builtins.type
|      Create and return a new object.  See help(type) for accurate signature.
|  
|  __next__(self, /)
|      Implement next(self).
|  
|  __reduce__(...)
|      Return state information for pickling.
None

  在Python3.x版本中,map是內建类的形式,如上面的代码所示。Python2.x版本中则是內建方法会出现,如:

Help on built-in function filter in module __builtin__:
filter(...)
   filter(function or None, sequence) -> list, tuple, or string
   Return those items of sequence for which function(item) is true.  If
   function is None, return the items that are true.  If sequence is a tuple
   or string, return the same type, else return a list.

  虽然Python2和3的表现形式不一样,但是基本功能相似,此处不做具体区分,有兴趣可以自己网上对比一下。我们直接看Python3,map是內建类能够返回map的obj(返回值是个迭代器)。第一个参数为一个可执行函数,第二个可变参数为可迭代对象(是一个或多个序列,取决于function需要几个参数)执行方式是,遍历迭代对象,将值对应放入可执行函数中进行映射处理,测试代码如下:

from collections import Iterator
m = map(lambda x, y : x + y, [1,2,3],[3,2,1])
print(type(m))
print("经过map之后是迭代器",isinstance(m,Iterator))
print(list(m))

运行结果:

<class 'map'>
经过map之后是迭代器 True
[4, 4, 4]

  其中第一个迭代对象中值对应的是函数参数x,第二个迭代对象对应的是函数参数y。

3.2 reduce

  Python3中,reduce函数从全局名字空间里移除, 被放置在fucntools模块中,所以使用之前需要引入,代码如下:

from functools import reduce
help(reduce)

运行结果:

Help on built-in function reduce in module _functools:
reduce(...)
   reduce(function, sequence[, initial]) -> value
   Apply a function of two arguments cumulatively to the items of a sequence,
   from left to right, so as to reduce the sequence to a single value.
   For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates
   ((((1+2)+3)+4)+5).  If initial is present, it is placed before the items
   of the sequence in the calculation, and serves as a default when the
   sequence is empty.

  第一个参数表示可执行函数,第二个参数表示一个序列(允许为str,tuple,list),initial表示初始值可以不写,功能表示依次从序列中取前两个值执行方法体内容,将结果返回与下一个值一起执行方法体。(有初始值时第一个元素和initial 作为参数调用函数)示例代码如下:

r = reduce(lambda x, y : x + y, [1,2,3],5)
#执行顺序为:((((5+1)+2)+3))
print(r)

3.3 filter

  filter在Python2和Python版本表现形式不同,可以类比map,,此处不做详细介绍。filter存在两个参数,第一个参数表示可执行函数,第二个参数为可迭代对象。功能表示对序列按照function进行过滤,为True保留,测试代码如下:

f = filter(lambda x : x % 2 == 0, [1,2,3,6,4,5])
print(list(f))

运行结果:

[2, 6, 4]

3.4 sorted

  sorted具有排序功能,我们来看看函数的构造:

Help on built-in function sorted in module builtins:
sorted(...)
   sorted(iterable, key=None, reverse=False) --> new sorted list

  sorted允许传入三个参数,其中第一个参数表示可迭代对象,第二个参数表示排序规则可自定义(默认常规的大小排序),第三个参数默认是从小到大排序(改成true反转排序),测试代码如下:

class People(object):
   """docstring for People"""
   def __init__(self, name,age):
       self.name = name
       self.age = age
#按照名字的排序方法
def sortName(p1):
   return p1.name
if __name__ == '__main__':
   people1 = People("zhangsan", 16)
   people2 = People("lisi", 14)
   people3 = People("wanger", 17)
   peoples = [people1,people2,people3]
   #排序
   s = sorted(peoples, key=sortName)
   #取出排序后的名称
   m = map(lambda x : x.name, s)
   print(list(m))

运行结果:

['lisi', 'wanger', 'zhangsan']

注:合理的使用内置的一些高阶函数会减少我们的代码书写量。

4、匿名函数

  常规的函数都是有名字的,但是如果我们写了个高阶函数,还得显示的定义一个函数,这样真的是太麻烦了。Python中有限的支持使用匿名函数,在一些简单的场景下我们可以使用匿名函数代替具名函数,匿名函数语法:lambda 参数1,参数2,... ,参数n :返回值,代码如下:

def sum(a, b, f):
   return f(a,b)

if __name__ == '__main__':
   print("匿名函数求和结果:" , sum(1, 2, lambda x, y : x + y))

  Python中的匿名函数形式:“lambda x, y : x + y”,对比我们之前的func函数,不难发现两者的功能是一致的。相比具名函数,匿名函数的代码量少,最重要的我们不要再去考虑如何命名。

  匿名函数也是函数,所以我们可以把匿名函数赋值给变量,如:

test = lambda x, y : print("输出值:" , x + y)
if __name__ == '__main__':
   test(1, 2)

  和具名函数不一样的是,匿名函数实际上只能算是表达式,只是具有函数的功能,匿名函数是不能像普通函数写方法体,他只有一行语句。所以只能应用于相对简单的场景中。

注:

  • 匿名函数是函数更是一个表达式,返回值中不允许书写代码块

  • 参数个数允许 >= 0,test2 = lambda : print("测试无参")

  • 必须有返回值,“print”的返回值类型是是NoneType,可以使用type测试类型

5、偏函数

  偏函数的功能有点类似我们之前看的传入默认参数,我们先看看默认参数情况下的代码:

#     圆面积,默认参数圆周率
def circleArea(r,PI = 3.14):
   print(r * r * PI)
if __name__ == '__main__':
   circleArea(3)

  改写成偏函数如下:

import functools
def circleArea(r,PI = 3.14):
   print(r * r * PI)
if __name__ == '__main__':
   circleArea(3)
   circleArea2 = functools.partial(circleArea,PI = 3.14)
   circleArea2(3)

  functools.partial可以为我们创建一个偏函数,其作用就是,把一个函数的某些参数给设置默认值,返回一个新的函数,调用起来会简单一些。我们一般会对内建函数使用偏函数设定一些初始值。

6、总结

  • Python中函数也是对象,允许被赋值给变量;

  • 合理使用高阶函数和匿名函数可以减少代码量;

  • 闭包的持有外部函数的局部变量的状态,提高代码的复用性,但是会占用内存。

推荐阅读:


以上是关于18Python函数式编程的主要内容,如果未能解决你的问题,请参考以下文章

Python函数式编程,范围和变量。我哪里错了?

函数式编程

写 Python 代码不可不知的函数式编程技术

函数式编程/命令式编程

python_函数式编程

《On Java 8》中文版 第十三章 函数式编程