装饰器还没搞懂,搞懂了再来继续写吧~
函数既可作为返回值也可作为函数的参数…一切都是对象,一切都是指针,一切都是东西…
返回函数(引用)和返回函数值是不一样滴…. ◡̈⃝︎⋆︎*
一. 高阶函数
- 变量可以指向函数;
- 函数名也是变量;
- 函数可以作为return的返回值;
- 函数可以作为另一个函数的参数。
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
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函数缩短代码。'''