sorted
排序算法
Python的内置sorted()函数可以对list进行排序:
1
2
|
>>>
sorted
([
1
,
10
,
2
,
5
,
42
,
6
])
[
1
,
2
,
5
,
6
,
10
,
42
]
|
此外,sorted()函数也是一个高阶函数,他还可以接受一个key函数来实现自定义的排序,例如按绝对值大小排序:
1
2
|
>>>
sorted
([
36
,
5
,
-
12
,
9
,
-
21
],key
=
abs
)
[
5
,
9
,
-
12
,
-
21
,
36
]
|
key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序。对比原始的list和进过key=abs 处理的list:
1
2
3
|
list
=
[
36
,
5
,
-
12
,
9
,
-
21
]
keys
=
[
36
,
5
,
12
,
9
,
21
]
|
然后sorted()
函数按照keys进行排序,并按照对应关系返回list相应的元素:
1
2
3
|
keys排序结果
=
> [
5
,
9
,
12
,
21
,
36
]
| | | | |
最终结果
=
> [
5
,
9
,
-
12
,
-
21
,
36
]
|
字符排序:
1
2
|
>>>
sorted
([
'bob'
,
'about'
,
'Zoo'
,
'Credit'
])
#根据ASCII的大小比较。
[
'Credit'
,
'Zoo'
,
'about'
,
'bob'
]
|
忽略大小写的字符串排序:
1
2
|
>>>
sorted
([
'bob'
,
'about'
,
'Zoo'
,
'Credit'
],key
=
str
.lower)
[
'about'
,
'bob'
,
'Credit'
,
'Zoo'
]
|
1
2
|
要进行反向排序,不必改动key函数,可以传入第三个函数
reverse
=
True
:
|
1
2
|
>>>
sorted
([
'bob'
,
'about'
,
'Zoo'
,
'Credit'
],key
=
str
.lower,reverse
=
True
)
[
'Zoo'
,
'Credit'
,
'bob'
,
'about'
]
|
1
|
L
=
[(
'Bob'
,
75
), (
'Adam'
,
92
), (
'Bart'
,
66
), (
'Lisa'
,
88
)]
|
请用sorted()
对上述列表分别按名字排序:
1
2
3
|
>>> L
=
[(
'Bob'
,
75
),(
'Adam'
,
92
),(
'Bart'
,
66
),(
'Lisa'
,
88
)]
>>>
def
by_name(t):
return
t[
0
].lower()
|
1
2
3
|
>>> L2
=
sorted
(L,key
=
by_name)
>>> L2
[(
'Adam'
,
92
), (
'Bart'
,
66
), (
'Bob'
,
75
), (
'Lisa'
,
88
)]
|
再按成绩从高到低排序:
1
2
3
4
5
6
|
>>> L
=
[(
'Bob'
,
75
),(
'Adam'
,
92
),(
'Bart'
,
66
),(
'Lisa'
,
88
)]
>>>
def
by_score(t):
return
t[
1
]
>>> L2
=
sorted
(L,key
=
by_score,reverse
=
True
)
>>> L2
[(
'Adam'
,
92
), (
'Lisa'
,
88
), (
'Bob'
,
75
), (
'Bart'
,
66
)]
|
返回函数--->函数作为返回值
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果返回。
普通情况下的求和函数:
1
2
3
4
5
6
7
|
>>>
def
calc_sum(
*
args):
ax
=
0
for
n
in
args:
ax
=
ax
+
n
return
ax
>>> calc_sum(
1
,
2
,
3
,
5
,
4
)
15
|
把函数作为返回值。( 如果不需要立刻求和,而是在后面的代码中,根据需要再计算怎么办?可以不返回求和的结果,而是返回求和的函数: )
1
2
3
4
5
6
7
8
9
10
11
12
|
>>>
def
lazy_sum(
*
args):
def
sum
():
ax
=
0
for
n
in
args:
ax
=
ax
+
n
return
ax
return
sum
>>> f
=
lazy_sum(
1
,
2
,
3
,
4
,
5
)
>>> f
<function lazy_sum.<
locals
>.
sum
at
0x026BBA08
>>>> f()
15
|
在上面的例子中,在函数 lazy_sum 中有定义了函数 sum ,并且,内部函数 sum 可以引用外部函数 lazy_sum 的参数和局部变量,当 laizy_sum 返回函数 sum 时, 相关参数和变量都保存在返回的函数中,这种称为 ‘闭包(Closure)’的程序结构拥有极大的威力。
当我们调用lazy_sum()
时,每次调用都会返回一个新的函数,即使传入相同的参数:
1
2
3
4
5
6
7
8
9
10
11
|
>>>
def
lazy_sum(
*
args):
def
sum
():
ax
=
0
for
n
in
args:
ax
=
ax
+
n
return
ax
return
sum
>>> f1
=
lazy_sum(
1
,
2
,
3
,
4
,
5
)
>>> f2
=
lazy_sum(
1
,
2
,
3
,
4
,
5
)
>>> f1
=
=
f2
False
|
f1()和 f2()的调用结果互不影响。
闭包
注意到返回的函数在其定义内部引用了局部变量args
,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,所以,闭包用起来简单,实现起来可不容易。
另一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()
才执行。我们来看一个例子:
1
2
3
4
5
6
7
8
|
>>>
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,但实际:
1
2
3
4
5
6
|
>>> f1()
9
>>> f2()
9
>>> f3()
9
|
结果全部都是9,原因在于返回的函数引用了变量 i ,但它并非立刻执行。等到3个函数都返回是,它们所引用的变量 i 应经变成了
3,因此最终结果为 9
返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
如果一定要引用循环变量怎么办?方法就是在创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何改变更改,已绑定到函数参数的值不变:
1
2
3
4
5
6
7
8
9
|
>>>
def
count():
def
f(j):
def
g():
return
j
*
j
return
g
fs
=
[]
for
i
in
range
(
1
,
4
):
fs.append(f(i))
return
fs
|
1
2
3
4
5
6
7
|
>>> f1,f2,f3
=
count()
>>> f1()
1
>>> f2()
4
>>> f3()
9
|
缺点就是 代码较长,可以利用lambda 函数缩短代码:
1
2
3
4
5
6
7
|
>>>
def
count():
def
f(j):
return
lambda
:j
*
j
fs
=
[]
for
i
in
range
(
1
,
4
):
fs.append(f(i))
return
fs
|
1
2
3
4
5
6
7
|
>>> f1,f2,f3
=
count()
>>> f1()
1
>
>> f2()
4
>>> f3()
9
|
匿名函数
在Python中,对匿名函数提供了有限支持。还是以map()
函数为例,计算f(x)=x2时,除了定义一个f(x)
的函数外,还可以直接传入匿名函数:
1
2
|
>>>
list
(
map
(
lambda
x:x
*
x,[
1
,
2
,
3
,
4
,
5
]))
[
1
,
4
,
9
,
16
,
25
]
|
通过例子可以看出匿名函数 lambda x:x*x实际上就是:
1
2
|
def
f(x):
return
x
*
x
|
关键字lambda表示匿名函数,冒号前面的x表示函数参数
匿名函数有个限制,就是只能有一个表达式,不用写return,表达式的值就是return
用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外。匿名函数也是一个函数对象,也可以吧匿名函数赋值给一个变量,再利用变量来调用这个函数:
1
2
3
4
|
>>> f
=
lambda
x:x
*
x
>>> f<function <
lambda
> at
0x02C9FD68
>>>> f(
5
)
25
|
同样也可以把匿名函数作为函数的返回值:
1
2
|
def
bulid(x,y):
return
lambda
: x
*
x
+
y
*
y
|