函数式编程|python的函数式编程

面向过程,面向对象

面向过程: 分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了
面向对象: 把问题中的事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述某个事物在整个解决问题的步骤中的行为
示例: 设计棋局
面向过程:

  • 开始游戏,红棋先走
  • 展示棋局情况
  • 判断战况(是否有棋子被吃、胜负情况)
  • 黑棋接着走
  • 展示棋局情况
  • 判断战况(是否有棋子被吃、胜负情况)
  • ……

面向对象:

  • 黑棋和红棋对象:负责棋子的走向,两者行为相同
  • 棋盘系统:通过黑棋和红棋对象的行为展示棋局情况
  • 规则系统:判断“战场”的变化以及胜负等

优缺点

面向过程
优点: 性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
缺点: 没有面向对象易维护、易复用、易扩展

面向对象
优点: 易维护、复用、扩展,由于面向对象所具有的封装、继承、多态的特性,可以设计出低耦合的系统,使系统更加灵活
缺点: 性能比面向过程低

函数式编程(Functional Programming)

函数是Python内建支持的一种封装,通过把大段代码拆成函数,通过一层一层的函数调用就可以把复杂任务分解成简单的任务,这种分解称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元
函数式编程也可以归结到面向过程的程序设计,但其思想更接近数学计算。
函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的

函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数

函数式编程中要求函数是一等公民
通常通过以下几点来判断函数是否是一等公民

  • 函数可以存储在变量中
  • 函数可以作为参数
  • 函数可以作为返回值

Python 的函数式编程

Python对函数式编程提供部分支持。由于Python允许使用变量,因此,Python不是纯函数式编程语言。

Python 对函数式编程提供部分支持。主要体现在下面几个方面:

  • Python 的一些语法,比如lambda、列表解析、字典解析、生成器、iter 等
  • Python 的一些内置函数,包括 map、reduce、filter、all、any、enumerate、zip 等
  • Python 的一些内置模块,比如 itertools、functools 和 operator 模块等
  • Python 的一些第三方库,比如 fn.py, toolz 等
  • Python 将函数视为“第一等公民” – 对象,因此可以作为函数参数也可以作为函数的返回值。包括高阶函数,返回函数,匿名函数,装饰器,偏函数等。

高阶函数

一个函数可以接收另一个函数作为参数,这种函数就称之为高阶函数(map, filter, reduce, sorted)

MapReduce: Simplified Data Processing on Large Clusters

map 映射函数

map(function, iterable, …)

  • 接收两个参数,第一个参数 function 以参数序列中的每一个元素调用 function 函数,
    返回包含每次 function 函数返回值的新列表
  • 返回值是一个迭代器
lst = [1,2,3,4,5,6,7]
lst2 = [10,100,1000,10000]
def f1(x,y):
    return x + y
map后面可以接受多个可迭代对象,那传入几个可迭代对象,前面的函数就要接受几个参数
print(list(map(f1,lst,lst2)))
print(list(map(lambda x,y:x+y, lst, lst2)))

例题:

有列表[1, 2, 3, 4, 5],将所有元素转换成str: [‘1’, ‘2’, ‘3’, ‘4’, ‘5’]

lst = [1,2,3,4,5]
print(list(map(str,lst)))

filter 过滤函数

filter(function, iterable)

  • 接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判断,
    然后返回 True 或 False,最后将返回True 的元素放到新列表中
  • 返回值是一个迭代器

例如:

去掉偶数,保留奇数[1,2,3,4,5,6,7,8,9]

print(list(filter(lambda x : x % 2, [1,2,3,4,5,6,7,8,9])))

在一个list中,删掉偶数,只保留奇数

lst=['A','','B',None,‘C’,' ','a'1,0]

print(list(filter(lambda x:x and str(x).strip(), lst)))

reduce() 函数会对参数序列中元素进行累积。(这个函数用的时候需要导入)

函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。

from functools import reduce
s = [1,3,5,7,9]
print(reduce(lambda x,y:x*10+y, s))

sorted 排序函数

sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序。

把一个序列中的字符串,忽略大小写排序

 list1=['bob','about','Zoo','Credit']
print(sorted(list1,key=lambda x:x.lower()))
print(sorted(list1,key=str.lower))

按value来排序

d1 = {"a":3,"b":4,"c":2,"d":5}
print(dict(sorted(d1.items(), key=lambda x:x[1])))

返回函数

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。

示例:

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum

在这个例子中,我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。

注意一点,当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数:

>>> f1 = lazy_sum(1, 3, 5, 7, 9)
>>> f2 = lazy_sum(1, 3, 5, 7, 9)
>>> f1==f2
False

f1()和f2()的调用结果互不影响。

闭包

当一个函数返回了一个函数后,其内部的局部变量还被新函数引用
需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行

def count():
    fs = []
    for i in range(1, 4):
        def f():
             return i*i
        fs.append(f)
    return fs

f1, f2, f3 = count()

实际结果是

>>> f1()
9
>>> f2()
9
>>> f3()
9

全部都是9!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。

返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:

def count():
    def f(j):
        def g():
            return j*j
        return g
    fs = []
    for i in range(1, 4):
        fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
    return fs

结果:

>>> f1, f2, f3 = count()
>>> f1()
1
>>> f2()
4
>>> f3()
9
nonlocal

使用闭包,就是内层函数引用了外层函数的局部变量。如果只是读外层变量的值,我们会发现返回的闭包函数调用一切正常:

def inc():
    x = 0
    def fn():
        # 仅读取x的值:
        return x + 1
    return fn

f = inc()
print(f()) # 1
print(f()) # 1

使用闭包时,对外层变量赋值前,需要先使用nonlocal声明该变量不是当前函数的局部变量。

匿名函数

匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。

用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数

>>> f = lambda x: x * x
>>> f
<function <lambda> at 0x101c6ef28>
>>> f(5)
25

同样,也可以把匿名函数作为返回值返回,比如:

def build(x, y):
    return lambda: x * x + y * y

装饰器

偏函数

functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单

>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85

你可能感兴趣的:(python,python,开发语言)