Python-函数式编程

装饰器还没搞懂,搞懂了再来继续写吧~
函数既可作为返回值也可作为函数的参数…一切都是对象,一切都是指针,一切都是东西…
返回函数(引用)和返回函数值是不一样滴…. ◡̈⃝︎⋆︎*



函数式编程




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



一. 高阶函数


  1. 变量可以指向函数
  2. 函数名也是变量
  3. 函数可以作为return的返回值
  4. 函数可以作为另一个函数的参数


1.1 变量指向函数


   在讲变量和字符串的时候我们讲过,变量就是一个对象,可以当作一个指针使用,而函数名也是一个变量,也就是一个对象。函数名其实就是指向函数的变量!注意,函数名是一个变量!变量!变量!当作指针用。

   既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数




见过两种函数赋值给变量的形式,这两种形式是有区别的,分别总结一下。
一种是

a = f

另一种是

a = f()



1. a = f 型属于将变量指向函数。

如下用示例说明:

print(abs(-10))
print(abs)

输出:
10
<built-in function abs>
------------------------------------------------------------------------------------------  
可见,abs(-10)是函数调用,而abs是函数本身。
要获得函数调用结果,我们可以把结果赋值给变量:  

x = abs(-10)
print(x)

输出:
10
------------------------------------------------------------------------------------------
但是,如果把函数本身赋值给变量呢?

f = abs
print(f)

输出:
<built-in function abs>
------------------------------------------------------------------------------------------
结论:函数本身也可以赋值给变量,即:变量可以指向函数。
如果一个变量指向了一个函数,那么,可否通过该变量来调用这个函数?用代码验证一下:

f = abs
print(f(-10))

输出:
10
------------------------------------------------------------------------------------------
成功!说明变量f现在已经指向了abs函数本身。直接调用abs()函数和调用变量f()完全相同。


说明变量f现在已经指向了abs函数本身。直接调用abs()函数和调用变量f()完全相同。这是廖雪峰老师python教程上的例子,现在调用f()和调用abs()是一样的了。


再举一个工厂函数的例子:

def maker(N):

  def action(X):

    return X**N

  return action
------------------------------------------------------------------------------------------
这个嵌套函数的外层返回值为内层函数的函数名,注意没有括号,这里有无括号是有很大区别的。此时调用外部函数:

f=maker(2)   //此时进入maker(2),且定义了一个action(X)函数,然后返回action函数名(对象)给f
------------------------------------------------------------------------------------------
那么如上所述,f便指向了action函数,且限制条件为N=2,可以理解为f为N等于2时的action函数。我们来调用它:

print(f(3))   //此时相当于调用了cation(3)函数,这也是在外部使用内部嵌套函数的方法,
              //因为内部嵌套函数是不允许在外部访问的。

输出:
9            //证明f和action函数是一样的。
------------------------------------------------------------------------------------------
如上的示例也可以用print(f=maker(2)(3))来输出结果一样,两个括号连在一起相当于执行了这两个函数。

def maker(N):
    def a(c): 
        return c**N 
    return a

f=maker(2)(3)
print(f)

OUTPUT: 9


2. a = f() 型属于将f()的返回值赋值给a的过程
这里的a仅仅接收f()的返回值,如果f()没有返回值,那么a即被赋值为None。这里值得注意的一点是,
在a=f()的执行过程中,f()会运行一次,如:

def add(x,y):
    z = x+y
    print(z)
a = add(3,4)
print('******我是分隔符,嘿嘿嘿******')
print(a)

OUTPUT: 
7
******我是分隔符,嘿嘿嘿******
None
------------------------------------------------------------------------------------------
这里在分隔符前输出了7,说明赋值过程函数add执行了,然而a的值为None,且只能通过print语句才可以显示。
这是因为add()函数没有return语句。



1.2 传入函数与返回函数名


一、传入函数(把函数作为参数)

  既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数

一个最简单的高阶函数:

def add(x, y, f):
    return f(x) + f(y)

当我们调用add(-5, 6, abs)时,参数x,y和f分别接收-5,6和abs,根据函数定义,我们可以推导计算过程为:

x = -5
y = 6
f = abs
f(x) + f(y) ==> abs(-5) + abs(6) ==> 11
return 11

用代码验证一下:

def add(x, y, f):
    return f(x) + f(y)

print(add(-5, 6, abs))

OUTPUT: 11 


总结:编写高阶函数,就是让函数的参数能够接收别的函数。把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式


二、返回函数名(把函数名作为返回值)

  高阶函数除了可以接受函数作为参数外,还可以把函数名作为结果值返回。注意Python3返回的是迭代器对象


'''我们来实现一个可变参数的求和。通常情况下,求和的函数是这样定义的:'''

def calc_sum(*args):   '在函数的参数章节讲过,*传入元组,**传入字典'
    ax = 0
    for n in args:
        ax = ax + n
    return ax
'''但是,如果不需要立刻求和,而是在后面的代码中,根据需要再计算怎么办?可以不返回求和的结果,
而是返回求和的函数:'''

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

'''当我们调用lazy_sum()时,返回的并不是求和结果,而是求和函数:'''

f = lazy_sum(1, 3, 5, 7, 9)
print(f)
OUTPUT: <function lazy_sum.<locals>.sum at 0x101c6ed90>

'''调用函数f时,才真正计算求和的结果:'''

f()
OUTPUT: 25

'''
在这个例子中,我们在函数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
OUTPUT: False
'f1()和f2()的调用结果互不影响。'


三、闭包

  注意到返回的函数在其定义内部引用了局部变量args,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,所以,闭包用起来简单,实现起来可不容易。

'''另一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行。
    我们来看一个例子:'''

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

f1, f2, f3 = count()

'''在上面的例子中,每次循环,都创建了一个新的函数,然后,把创建的3个函数都返回了。
你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果是:'''

>>> 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
'''缺点是代码较长,可利用lambda函数缩短代码。'''



二. 装饰器


  Reprint please specify: Treecatee Python-函数式编程

 Previous
Markdown Markdown
1.Markdown在线编辑器 2.Markdown高级语法大全 3.Markdown高级语法
2019-03-06 Treecatee
Next 
Python-模块和包 Python-模块和包
from…import…😀 模块和包 一. 模块与包的意义 1.1 什么是模块? 在Python中,一个.py文件就称之为一个模块(Module)。 1.2 为什么要使用模块? 为了编写可维护的代码,我们把很多函数分组,分
2019-03-03
  TOC