mirror of
https://github.com/apachecn/ailearning.git
synced 2026-02-11 06:15:22 +08:00
535 lines
6.8 KiB
Markdown
535 lines
6.8 KiB
Markdown
# 函数进阶:参数传递,高阶函数,lambda 匿名函数,global 变量,递归
|
||
|
||
## 函数是基本类型
|
||
|
||
在 `Python` 中,函数是一种基本类型的对象,这意味着
|
||
|
||
* 可以将函数作为参数传给另一个函数
|
||
* 将函数作为字典的值储存
|
||
* 将函数作为另一个函数的返回值
|
||
|
||
In [1]:
|
||
|
||
```py
|
||
def square(x):
|
||
"""Square of x."""
|
||
return x*x
|
||
|
||
def cube(x):
|
||
"""Cube of x."""
|
||
return x*x*x
|
||
|
||
```
|
||
|
||
作为字典的值:
|
||
|
||
In [2]:
|
||
|
||
```py
|
||
funcs = {
|
||
'square': square,
|
||
'cube': cube,
|
||
}
|
||
|
||
```
|
||
|
||
例子:
|
||
|
||
In [3]:
|
||
|
||
```py
|
||
x = 2
|
||
|
||
print square(x)
|
||
print cube(x)
|
||
|
||
for func in sorted(funcs):
|
||
print func, funcs[func](x)
|
||
|
||
```
|
||
|
||
```py
|
||
4
|
||
8
|
||
cube 8
|
||
square 4
|
||
|
||
```
|
||
|
||
## 函数参数
|
||
|
||
### 引用传递
|
||
|
||
`Python` 中的函数传递方式是 `call by reference` 即引用传递,例如,对于这样的用法:
|
||
|
||
```py
|
||
x = [10, 11, 12]
|
||
f(x)
|
||
```
|
||
|
||
传递给函数 `f` 的是一个指向 `x` 所包含内容的引用,如果我们修改了这个引用所指向内容的值(例如 `x[0]=999`),那么外面的 `x` 的值也会被改变。不过如果我们在函数中赋给 `x` 一个新的值(例如另一个列表),那么在函数外面的 `x` 的值不会改变:
|
||
|
||
In [4]:
|
||
|
||
```py
|
||
def mod_f(x):
|
||
x[0] = 999
|
||
return x
|
||
|
||
x = [1, 2, 3]
|
||
|
||
print x
|
||
print mod_f(x)
|
||
print x
|
||
|
||
```
|
||
|
||
```py
|
||
[1, 2, 3]
|
||
[999, 2, 3]
|
||
[999, 2, 3]
|
||
|
||
```
|
||
|
||
In [5]:
|
||
|
||
```py
|
||
def no_mod_f(x):
|
||
x = [4, 5, 6]
|
||
return x
|
||
|
||
x = [1,2,3]
|
||
|
||
print x
|
||
print no_mod_f(x)
|
||
print x
|
||
|
||
```
|
||
|
||
```py
|
||
[1, 2, 3]
|
||
[4, 5, 6]
|
||
[1, 2, 3]
|
||
|
||
```
|
||
|
||
### 默认参数是可变的!
|
||
|
||
函数可以传递默认参数,默认参数的绑定发生在函数定义的时候,以后每次调用默认参数时都会使用同一个引用。
|
||
|
||
这样的机制会导致这种情况的发生:
|
||
|
||
In [6]:
|
||
|
||
```py
|
||
def f(x = []):
|
||
x.append(1)
|
||
return x
|
||
|
||
```
|
||
|
||
理论上说,我们希望调用 `f()` 时返回的是 `[1]`, 但事实上:
|
||
|
||
In [7]:
|
||
|
||
```py
|
||
print f()
|
||
print f()
|
||
print f()
|
||
print f(x = [9,9,9])
|
||
print f()
|
||
print f()
|
||
|
||
```
|
||
|
||
```py
|
||
[1]
|
||
[1, 1]
|
||
[1, 1, 1]
|
||
[9, 9, 9, 1]
|
||
[1, 1, 1, 1]
|
||
[1, 1, 1, 1, 1]
|
||
|
||
```
|
||
|
||
而我们希望看到的应该是这样:
|
||
|
||
In [8]:
|
||
|
||
```py
|
||
def f(x = None):
|
||
if x is None:
|
||
x = []
|
||
x.append(1)
|
||
return x
|
||
|
||
print f()
|
||
print f()
|
||
print f()
|
||
print f(x = [9,9,9])
|
||
print f()
|
||
print f()
|
||
|
||
```
|
||
|
||
```py
|
||
[1]
|
||
[1]
|
||
[1]
|
||
[9, 9, 9, 1]
|
||
[1]
|
||
[1]
|
||
|
||
```
|
||
|
||
## 高阶函数
|
||
|
||
以函数作为参数,或者返回一个函数的函数是高阶函数,常用的例子有 `map` 和 `filter` 函数:
|
||
|
||
`map(f, sq)` 函数将 `f` 作用到 `sq` 的每个元素上去,并返回结果组成的列表,相当于:
|
||
|
||
```py
|
||
[f(s) for s in sq]
|
||
|
||
```
|
||
|
||
In [9]:
|
||
|
||
```py
|
||
map(square, range(5))
|
||
|
||
```
|
||
|
||
Out[9]:
|
||
|
||
```py
|
||
[0, 1, 4, 9, 16]
|
||
```
|
||
|
||
`filter(f, sq)` 函数的作用相当于,对于 `sq` 的每个元素 `s`,返回所有 `f(s)` 为 `True` 的 `s` 组成的列表,相当于:
|
||
|
||
```py
|
||
[s for s in sq if f(s)]
|
||
|
||
```
|
||
|
||
In [10]:
|
||
|
||
```py
|
||
def is_even(x):
|
||
return x % 2 == 0
|
||
|
||
filter(is_even, range(5))
|
||
|
||
```
|
||
|
||
Out[10]:
|
||
|
||
```py
|
||
[0, 2, 4]
|
||
```
|
||
|
||
一起使用:
|
||
|
||
In [11]:
|
||
|
||
```py
|
||
map(square, filter(is_even, range(5)))
|
||
|
||
```
|
||
|
||
Out[11]:
|
||
|
||
```py
|
||
[0, 4, 16]
|
||
```
|
||
|
||
`reduce(f, sq)` 函数接受一个二元操作函数 `f(x,y)`,并对于序列 `sq` 每次合并两个元素:
|
||
|
||
In [12]:
|
||
|
||
```py
|
||
def my_add(x, y):
|
||
return x + y
|
||
|
||
reduce(my_add, [1,2,3,4,5])
|
||
|
||
```
|
||
|
||
Out[12]:
|
||
|
||
```py
|
||
15
|
||
```
|
||
|
||
传入加法函数,相当于对序列求和。
|
||
|
||
返回一个函数:
|
||
|
||
In [13]:
|
||
|
||
```py
|
||
def make_logger(target):
|
||
def logger(data):
|
||
with open(target, 'a') as f:
|
||
f.write(data + '\n')
|
||
return logger
|
||
|
||
foo_logger = make_logger('foo.txt')
|
||
foo_logger('Hello')
|
||
foo_logger('World')
|
||
|
||
```
|
||
|
||
In [14]:
|
||
|
||
```py
|
||
!cat foo.txt
|
||
|
||
```
|
||
|
||
```py
|
||
Hello
|
||
World
|
||
|
||
```
|
||
|
||
In [15]:
|
||
|
||
```py
|
||
import os
|
||
os.remove('foo.txt')
|
||
|
||
```
|
||
|
||
## 匿名函数
|
||
|
||
在使用 `map`, `filter`,`reduce` 等函数的时候,为了方便,对一些简单的函数,我们通常使用匿名函数的方式进行处理,其基本形式是:
|
||
|
||
```py
|
||
lambda <variables>: <expression>
|
||
```
|
||
|
||
例如,我们可以将这个:
|
||
|
||
In [16]:
|
||
|
||
```py
|
||
print map(square, range(5))
|
||
|
||
```
|
||
|
||
```py
|
||
[0, 1, 4, 9, 16]
|
||
|
||
```
|
||
|
||
用匿名函数替换为:
|
||
|
||
In [17]:
|
||
|
||
```py
|
||
print map(lambda x: x * x, range(5))
|
||
|
||
```
|
||
|
||
```py
|
||
[0, 1, 4, 9, 16]
|
||
|
||
```
|
||
|
||
匿名函数虽然写起来比较方便(省去了定义函数的烦恼),但是有时候会比较难于阅读:
|
||
|
||
In [18]:
|
||
|
||
```py
|
||
s1 = reduce(lambda x, y: x+y, map(lambda x: x**2, range(1,10)))
|
||
print(s1)
|
||
|
||
```
|
||
|
||
```py
|
||
285
|
||
|
||
```
|
||
|
||
当然,更简单地,我们可以写成这样:
|
||
|
||
In [19]:
|
||
|
||
```py
|
||
s2 = sum(x**2 for x in range(1, 10))
|
||
print s2
|
||
|
||
```
|
||
|
||
```py
|
||
285
|
||
|
||
```
|
||
|
||
# global 变量
|
||
|
||
一般来说,函数中是可以直接使用全局变量的值的:
|
||
|
||
In [20]:
|
||
|
||
```py
|
||
x = 15
|
||
|
||
def print_x():
|
||
print x
|
||
|
||
print_x()
|
||
|
||
```
|
||
|
||
```py
|
||
15
|
||
|
||
```
|
||
|
||
但是要在函数中修改全局变量的值,需要加上 `global` 关键字:
|
||
|
||
In [21]:
|
||
|
||
```py
|
||
x = 15
|
||
|
||
def print_newx():
|
||
global x
|
||
x = 18
|
||
print x
|
||
|
||
print_newx()
|
||
|
||
print x
|
||
|
||
```
|
||
|
||
```py
|
||
18
|
||
18
|
||
|
||
```
|
||
|
||
如果不加上这句 `global` 那么全局变量的值不会改变:
|
||
|
||
In [22]:
|
||
|
||
```py
|
||
x = 15
|
||
|
||
def print_newx():
|
||
x = 18
|
||
print x
|
||
|
||
print_newx()
|
||
|
||
print x
|
||
|
||
```
|
||
|
||
```py
|
||
18
|
||
15
|
||
|
||
```
|
||
|
||
## 递归
|
||
|
||
递归是指函数在执行的过程中调用了本身,一般用于分治法,不过在 `Python` 中这样的用法十分地小,所以一般不怎么使用:
|
||
|
||
Fibocacci 数列:
|
||
|
||
In [23]:
|
||
|
||
```py
|
||
def fib1(n):
|
||
"""Fib with recursion."""
|
||
|
||
# base case
|
||
if n==0 or n==1:
|
||
return 1
|
||
# recurssive caae
|
||
else:
|
||
return fib1(n-1) + fib1(n-2)
|
||
|
||
print [fib1(i) for i in range(10)]
|
||
|
||
```
|
||
|
||
```py
|
||
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
|
||
|
||
```
|
||
|
||
一个更高效的非递归版本:
|
||
|
||
In [24]:
|
||
|
||
```py
|
||
def fib2(n):
|
||
"""Fib without recursion."""
|
||
a, b = 0, 1
|
||
for i in range(1, n+1):
|
||
a, b = b, a+b
|
||
return b
|
||
|
||
print [fib2(i) for i in range(10)]
|
||
|
||
```
|
||
|
||
```py
|
||
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
|
||
|
||
```
|
||
|
||
速度比较:
|
||
|
||
In [25]:
|
||
|
||
```py
|
||
%timeit fib1(20)
|
||
%timeit fib2(20)
|
||
|
||
```
|
||
|
||
```py
|
||
100 loops, best of 3: 5.35 ms per loop
|
||
100000 loops, best of 3: 2.2 µs per loop
|
||
|
||
```
|
||
|
||
对于第一个递归函数来说,调用 `fib(n+2)` 的时候计算 `fib(n+1), fib(n)`,调用 `fib(n+1)` 的时候也计算了一次 `fib(n)`,这样造成了重复计算。
|
||
|
||
使用缓存机制的递归版本,这里利用了默认参数可变的性质,构造了一个缓存:
|
||
|
||
In [26]:
|
||
|
||
```py
|
||
def fib3(n, cache={0: 1, 1: 1}):
|
||
"""Fib with recursion and caching."""
|
||
|
||
try:
|
||
return cache[n]
|
||
except KeyError:
|
||
cache[n] = fib3(n-1) + fib3(n-2)
|
||
return cache[n]
|
||
|
||
print [fib3(i) for i in range(10)]
|
||
|
||
%timeit fib1(20)
|
||
%timeit fib2(20)
|
||
%timeit fib3(20)
|
||
|
||
```
|
||
|
||
```py
|
||
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
|
||
100 loops, best of 3: 5.37 ms per loop
|
||
100000 loops, best of 3: 2.19 µs per loop
|
||
The slowest run took 150.16 times longer than the fastest. This could mean that an intermediate result is being cached
|
||
1000000 loops, best of 3: 230 ns per loop
|
||
|
||
``` |