函数¶
对于变量,赋值完成后,修改赋值语句中其他变量的值,这个变量的值不会因为别的变量改变而自动变化;对于函数,同一个函数 f() ,因为它内部用到了外部变量 a、b 等等,改变这些变量后,再调用函数,返回值就可能变。
评估(Evaluation)¶
Python 解释器读取一段代码,进行计算,并最终得出一个“结果对象”的过程。内存中制造出了一个“函数对象”,但代码没有运行。
内部名称¶
当定义 def square(x): ... 时,Python 实际上做了两件事:在内存中创建一个函数对象,它的名字叫 square;在当前的环境中创建一个变量名 square,并指向这个函数对象。可以通过函数的 .__name__ 属性看到这个内部名称:
重名¶
有重名时,先检查本地(局部)帧(框架),再检查全局帧(框架)。例如:
当一个内置函数名称与变量名称冲突时,优先使用变量。例如:
内置函数可以作为另一个内置函数的变量名。例如:max=min,调用max(1,2)时会调用min函数。
当后面定义了一个与前面函数同名的函数时,优先使用后定义的函数。
多重返回值¶
return语句可以返回多个值。例如:
>>> def divide_exact(n, d):
... return n // d, n % d
>>> quotient, remainder = divide_exact(2013, 10)
高阶函数:接受另一个函数作为参数的函数¶
函数作为形参¶
在具有多个形参的函数A中,某些形参可能是函数B。在调用函数A时,一定会自动执行函数B。例如:
def if_(c, t, f):
if c:
return t
else:
return f
from math import sqrt
def real_sqrt(x):
"""Return the real part of the square root of x."""
return if_(x >= 0, sqrt(x), 0)
在执行return if_(x >= 0, sqrt(x), 0)时,函数if_会被调用,并且参数t会被求值为sqrt(x)。这意味着如果x是负数,sqrt(x)仍然会被计算,从而导致报错。
函数作为返回值¶
def make_adder(n):
"""Return a function that takes one argument k and returns k + n."""
def adder(k):
return k + n
return adder
想计算4 + 3,可以调用make_adder(3)(4)
adder的父级是make_adder,make_adder的父级是全局帧。
闭包¶
通常情况下,一个函数运行结束,它内部的局部变量就会被销毁。但在闭包下,即使外部函数已经执行完了,内部函数依然能记住并访问外部函数里的变量。
要形成一个闭包,必须满足以下三点:
- 必须有嵌套函数(函数里面定义函数)。
- 内部函数必须引用外部函数的变量。
- 外部函数必须返回内部函数。
例如:
当执行 f = delay(6) 时:
delay执行完毕。- 返回的函数
f(也就是g)其实随身携带了参数6。 - 所以当执行
f()时,返回6。
环境图¶
Frame:
Object:
Frame 代表函数调用时的环境,Object 代表内存中的对象。Frame中的变量会指向Object。
调用时,从子级向上,例如上面的代码从adder向上找到make_adder,再从make_adder向上找到全局帧。
当函数被调用时,会创建一个新的局部帧,绑定它的父级为定义该函数时所在的帧,传递参数(当前帧的变量),执行函数。
Lambda 函数¶
lambda函数是匿名函数,可以看作是高阶函数的特例。例如:square = lambda x: x * x,此时square被绑定为了x的平方这样一个函数。也可以直接用(lambda x:x * x)(3),直接调用lambda函数。
图片里写的是 Function Currying(函数柯里化)。这是函数式编程里的一个重要概念,我用直观 + 例子给你解释。
函数柯里化(单参数函数链)¶
把一个“接收多个参数的函数”,变成一连串每次只接收一个参数的函数。
装饰器¶
Python 装饰器(Decorator) 允许在不修改原有函数代码的情况下,给函数添加新的功能。
装饰器本质上是一个闭包,它接收一个函数作为参数,并返回一个新的函数。使用 @ 符号来使用装饰器。例如:
def my_decorator(func):
def wrapper():
print("--- 执行函数前:准备工作 ---")
func()
print("--- 执行函数后:清理工作 ---")
return wrapper
@my_decorator
def say_hello():
print("你好,世界!")
say_hello()
当使用 @my_decorator 作用在 say_hello 函数上时,Python 实际上执行了以下操作:say_hello = my_decorator(say_hello),原有的 say_hello 函数被替换成了 wrapper 函数。当调用 say_hello() 时,实际上是在执行 wrapper()。
递归¶
交叉递归¶
交叉递归是指两个或多个函数相互调用对方来实现递归的过程。例如,计算一个数是否为偶数或奇数:
def is_even(n):
if n == 0:
return True
else:
return is_odd(n - 1)
def is_odd(n):
if n == 0:
return False
else:
return is_even(n - 1)
树形递归¶
树形递归是指一个函数在执行过程中会多次调用自身,从而形成树状的调用结构。例如,计算斐波那契数列:
匿名函数递归¶
这个表达式是一个函数lambda_f,这个函数会返回一个把自己的参数作为其中一个参数的函数,它相当于:
这个技巧可以用来实现匿名函数的递归调用。例如,计算阶乘:
后半段相当于:
当使用lambda_f(factorial)(n)时,它会先返回lambda_x,然后运行lambda_x(n),即factorial(factorial, n)从而使得参数f始终为factorial函数本身,实现递归调用。