函数与函数编程#
函数#
当调用一个函数时,它的参数是按引用传递给。如果函数的实参一个可变对象(如列表或字典),则函数内对该对象的修改将会影响到函数之外。例如:
a = [1, 2, 3, 4, 5]
def foo(x):
global b # 声明全局变量
b = 10
x[3] = -33 # 修改 x 中的元素
foo(a)
print(a)
print(b)
[1, 2, 3, -33, 5]
10
如果没有指定返回对象或者 return
语句被省略,则会返回一个 None
对象。如果要返回多个值,可以通过返回一个元组或其它包含对象来完成。
带默认参数的函数#
def repeat_str(s, times=1):
repeated_strs = s * times
return repeated_strs
repeated_strings = repeat_str("Happy Birthday! ")
print(repeated_strings)
repeated_strings_2 = repeat_str("Happy Birthday! " , 4)
print(repeated_strings_2)
Happy Birthday!
Happy Birthday! Happy Birthday! Happy Birthday! Happy Birthday!
关键字参数#
def func(a, b=40, c=80):
print('a:', a, ', b:', b, ', c:', c)
func(13, 17)
func(12, c=24) # 指定给哪个关键字赋值
func(c=40, a=80) # 指定给 a 赋值,可以把它放在后面
a: 13 , b: 17 , c: 80
a: 12 , b: 40 , c: 24
a: 80 , b: 40 , c: 40
可变长参数的函数#
*args
可变长度元组参数**kwargs
可变长度字典参数
def var_paras(fpara, *args, **kwargs):
print("fpara: " + str(fpara))
print("args: " + str(args))
print("kwargs: " + str(kwargs))
print()
var_paras("hello1")
var_paras("hello2", 1, 3, 5)
var_paras("hello3", ele="world3")
var_paras("hello4", 1, 3, ele="world4")
fpara: hello1
args: ()
kwargs: {}
fpara: hello2
args: (1, 3, 5)
kwargs: {}
fpara: hello3
args: ()
kwargs: {'ele': 'world3'}
fpara: hello4
args: (1, 3)
kwargs: {'ele': 'world4'}
递归#
Python 对递归函数调用的次数作了限制。函数 sys.getrecursionlimit()
返回当前允许的最大递归次数,而函数 sys.setrecursionlimit()
可以改变该函数的返回值。
默认的最大递归次数为1000,当一个函数递归次数超过最大递归次数时,就会引发 RuntimeError
异常.
lambda 操作符#
lambda
语句用来创建一个匿名函数(没和名字绑定的函数):
lambda args: expression
args
是一个用逗号分隔的参数,expression
是一个调用这些参数的表达式。例如:
a = lambda x, y: x + y
print(a(2, 3))
5
lambda
定义的代码必须是一个合法的表达式。多重语句和其他非表达式语句(如 print
、for
、while
等)不能出现在 lambda
语句中。lambda
表达式也遵循和函数一样的作用域规则。
map() 函数#
t = map(func, s)
函数将序列 s
中的每个元素传递给 func
函数做参数, 函数的返回值组成了列表 t
。 即 t[i] = func(s[i])
。需要注意的是, func
函数必须有只有一个参数,例如:
a = [1, 2, 3, 4, 5, 6]
def foo(x):
return 3 * x
b = map(foo, a) # b = [3, 6, 9, 12, 15, 18]
b
<map at 0x7f98bc8ac880>
上边的例子中的函数也可以用匿名函数来创建:
b = map(lambda x: 3 * x, a) # b = [3, 6, 9, 12, 15, 18]
b
<map at 0x7f98bc8ac310>
map()
函数也可以用于多个列表,如 t = map(func, s1, s2, ..., sn)
。 如果是这种形式,t
中的每个元素 t[i] = func(s1[i], s2[i], ..., sn[i])
。func
函数的形参个数必须和列表的个数 n
相同,结果与 s1, s2, ..., sn
中的最长的列表的元素个数相同。在计算过程中,短的列表自动用 None
扩充为统一长度的列表。
如果函数 func
为 None
,则 func
就被当成是恒等函数处理。这样函数就返回一个包含元组的列表:
a = [1, 2, 3, 4]
b = [100, 101, 102, 103]
c = map(None, a, b) # c = [(1,100), (2,101), (3,102), (4,103)]
c
<map at 0x7f98bc8ac190>
zip() 函数#
上边这个例子也可以用 zip(s1, s2, ..., sn)
函数来完成。zip()
用来将几个序列组合成一个包含元组的序列,序列中的每个元素 t[i] = (s1[i], s2[i], ..., sn[i])
。 与 map()
不同的是, zip()
函数将所有较长的序列序列截的和最短序列一样长:
d = [1, 2, 3, 4, 5, 6, 7]
e = [10, 11, 12]
f = zip(d, e) # f = [(1, 10), (2, 11), (3, 12)]
reduce() 函数#
reduce(func, s)
函数从一个序列收集信息,然后只返回一个值(例如求和,最大值等)。它首先以序列的前两个元素调用函数,再将返回值和第三个参数作为参数调用函数,依次执行下去,返回最终的值。 func
函数有且只有两个参数。例如:
from functools import reduce
def sum(x, y):
return x + y
b = reduce(sum, a)
b # b = (((1 + 2) + 3) + 4) = 10
10
filter() 函数#
filter(func, s)
是个序列过虑器,它使用 func()
函数来过滤 s
中的元素。使 func
返回值为 false
的元素被丢弃,其它的存入 filter
函数返回的列表中,例如:
c = filter(lambda x: x < 4, a) # c = [1, 2, 3]
如果函数 func
为 None
,则 func
就被当成是恒等函数处理。这样,函数就返回序列 s
中值为 True
的元素。
列表内涵#
列表内涵可以代替许多调用 map()
和 filter()
函数的操作。列表内涵的一般形式是:
[表达式 for item1 in 序列1
for item2 in 序列2
...
for itemN in 序列N
if 条件表达式]
上边的例子等价于:
s = []
for item1 in sequence1:
for item2 in sequence2:
...
for itemN in sequenceN:
if condition: s.append(expression)
下面这个代码片段可以帮助你理解列表内涵:
import math
from functools import reduce
a = [-3, 5, 2, -10, 7, 8]
b = "abc"
c = [2 * s for s in a] # c = [-6,10,4,-20,14,16]
d = [s for s in a if s >= 0] # d = [5,2,7,8]
e = [ # e = [(5,'a'),(5,'b'),(5,'c'),
(x, y) # (2,'a'),(2,'b'),(2,'c'),
for x in a # (7,'a'),(7,'b'),(7,'c'),
for y in b # (8,'a'),(8,'b'),(8,'c')]
if x > 0
]
f = [(1, 2), (3, 4), (5, 6)] # f = [2.23606, 5.0, 7.81024]
g = [math.sqrt(x * x + y * y) for x, y in f]
h = reduce(lambda x, y: x + y, [math.sqrt(x * x + y * y) for x, y in f]) # 平方根的和
提供给列表内涵的序列不必等长,因为系统内部使用嵌套的一系列 for
循环来迭代每个序列中的每个元素,然后由 if
从句处理条件表达式,若条件表达式为真,计算表达式的值并放入到列表内涵返回的序列中。 if
从句是可选的。
当使用列表内涵来构建包含元组的列表时,元组的值必须放在括号里。例如 [(x, y) for x in a for y in b]
是一个合法的语句,而 [x, y for x in a for y in b]
则不是。
最后,你应该注意在一个列表内涵中定义的变量是与列表内涵本身有同样的作用域,在列表内涵计算完成后会继续存在。例如 [x for x in a]
会覆盖内涵外先前定义的 x
,最终 x
的值会是 a
中的最后一个元素的值。
eval()、exec 和 execfile()#
eval(str [, globals [, locals ]])
函数将字符串 str
当成有效 Python 表达式来求值,并返回计算结果。
eval("3 + 4")
7
同样地, exec
语句将字符串 str
当成有效 Python 代码来执行。提供给 exec
的代码的名称空间和 exec
语句的名称空间相同。
exec("a = 100")
a
100
最后,execfile(filename [, globals [, locals ]])
函数可以用来执行一个文件,看下面的例子:
import os
import sys
sys.path.append(os.getcwd() + "/modules_and_packages")
execfile(r"modules_and_packages/myprogram.py")
Hey I am in some_main_script in main package.
Hey Im a function inside mysubscript
默认地,eval()
、exec
、execfile()
所运行的代码都位于当前的名字空间中。eval()
、exec
、execfile()
函数也可以接受一个或两个可选字典参数作为代码执行的全局名字空间和局部名字空间。 例如:
%%writefile ../_tmp/function_exec.py
result = 3 * x + 4 * y
print(result)
for b in birds:
print(b)
Writing ../_tmp/function_exec.py
globals = {"x": 7, "y": 10, "birds": ["Parrot", "Swallow", "Albatross"]}
locals = {}
# 将上边的字典作为全局和局部名称空间
a = eval("3 * x + 4 * y", globals, locals)
exec("for b in birds: print(b)", globals, locals)
exec(open("../_tmp/function_exec.py").read(), globals, locals)
Parrot
Swallow
Albatross
61
Parrot
Swallow
Albatross
如果你省略了一个或者两个名称空间参数,那么当前的全局和局部名称空间就被使用。如果一个函数体内嵌嵌套函数或 lambda
匿名函数时,同时又在函数主体中使用 exec
或 execfile()
函数时, 由于牵到嵌套作用域,会引发一个 SyntaxError
异常
注意例子中 exec
语句的用法和 eval()
、execfile()
是不一样的。 exec
是一个语句(就像 print
或 while
),而 eval()
和 execfile()
则是内建函数。
exec(str)
这种形式也被接受,但是它没有返回值。
当一个字符串被 eval()
、exec
、execfile()
执行时,解释器会先将它们编译为字节代码,然后再执行。这个过程比较耗时,所以如果需要对某段代码执行很多次时,最好还是对该代码先进行预编译,这样就不需要每次都编译一遍代码,可以有效提高程序的执行效率。
compile()#
compile(str, filename, kind)
函数将一个字符串编译为字节代码, str
是将要被编译的字符串, filename
是定义该字符串变量的文件,kind
参数指定了代码被编译的类型('single'
指单个语句,'exec'
指多个语句,'eval'
指一个表达式)。cmpile()
函数返回一个代码对象,该对象当然也可以被传递给 eval()
函数和 exec
语句来执行,例如:
x = 7
y = 10
str1 = "for i in range(0, 10): print(i)"
c1 = compile(str1, "", "exec") # 编译为字节代码对象
exec(c1) # 执行
str2 = "3 * x + 4 * y"
c2 = compile(str2, "", "eval") # 编译为表达式
result = eval(c2) # 执行
print(result)
0
1
2
3
4
5
6
7
8
9
61