python-函数
函数参数
参考地址:http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001374738449338c8a122a7f2e047899fc162f4a7205ea3000
只说明跟c++不同的地方,相同的地方就不啰嗦
默认参数
可变参数
就是说传入的参数的个数是可变,我们可以让传入参数是元组,列表等,这样元素的个数就也是可变的了,但这样还要先定义好一个元组或列表,使用可变参数,可以传入任意个独立的参数,可以是0个或多个,而可变参数会将这些参数整理成一个元组进行整体的处理,定义可变参数的形式如下
def fun(*val): for k in val: print k fun(1,2,3)
如果已经有一个元组或列表了,则可以这样调用
a = [1,2,3]
fun(a[0],a[1],a[2])
fun(*a)
关键字参数
可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自装为一个dict。
def person(name, age, **kw): print 'name:', name, 'age:', age, 'other:', kw
>>> person('Michael', 30)
name: Michael age: 30 other: {}
>>> person('Adam', 45, gender='M', job='Engineer')
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
关键字参数有什么用?它可以扩展函数的功能。比如,在person
函数里,我们保证能接收到name
和age
这两个参数,但是,如果调用者愿意提供更多的参数,我们也能收到。试想你正在做一个用户注册的功能,除了用户名和年龄是必填项外,其他都是可选项,利用关键字参数来定义这个函数就能满足注册的需求。
和可变参数类似,也可以先组装出一个dict,然后,把该dict转换为关键字参数传进去:
>>> kw = {'city': 'Beijing', 'job': 'Engineer'} >>> person('Jack', 24, city=kw['city'], job=kw['job']) name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
当然,上面复杂的调用可以用简化的写法:
>>> kw = {'city': 'Beijing', 'job': 'Engineer'} >>> person('Jack', 24, **kw) name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
参数组合
在Python中定义函数,可以用必选参数、默认参数、可变参数和关键字参数,这4种参数都可以一起使用,或者只用其中某些,但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数和关键字参数。
比如定义一个函数,包含上述4种参数:
def func(a, b, c=0, *args, **kw): print 'a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw
在函数调用的时候,Python解释器自动按照参数位置和参数名把对应的参数传进去。
>>> func(1, 2) a = 1 b = 2 c = 0 args = () kw = {} >>> func(1, 2, c=3) a = 1 b = 2 c = 3 args = () kw = {} >>> func(1, 2, 3, 'a', 'b') a = 1 b = 2 c = 3 args = ('a', 'b') kw = {} >>> func(1, 2, 3, 'a', 'b', x=99) a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
最神奇的是通过一个tuple和dict,你也可以调用该函数:
>>> args = (1, 2, 3, 4) >>> kw = {'x': 99} >>> func(*args, **kw) a = 1 b = 2 c = 3 args = (4,) kw = {'x': 99}
所以,对于任意函数,都可以通过类似func(*args, **kw)
的形式调用它,无论它的参数是如何定义的。
函数嵌套
所谓的函数嵌套,就是指函数里面又有函数,比如:
def f1(): print('hello') def f2(): print('world') f2() f1() # 输出 hello world
其实,函数的嵌套,主要有下面两个方面的作用。
第一,函数的嵌套能够保证内部函数的隐私。
内部函数只能被外部函数所调用和访问,不会暴露在全局作用域,因此,如果你的函数内部有一些隐私数据(比如数据库的用户、密码等),不想暴露在外,那你就可以使用函数的的嵌套,将其封装在内部函数中,只通过外部函数来访问。比如:
def connect_DB(): def get_DB_configuration(): ... return host, username, password conn = connector.connect(get_DB_configuration()) return conn
第二,合理的使用函数嵌套,能够提高程序的运行效率。
def factorial(input): # validation check if not isinstance(input, int): raise Exception('input must be an integer.') if input < 0: raise Exception('input must be greater or equal to 0' ) ... def inner_factorial(input): if input <= 1: return 1 return input * inner_factorial(input-1) return inner_factorial(input) print(factorial(5))
这里,我们使用递归的方式计算一个数的阶乘。因为在计算之前,需要检查输入是否合法,所以我写成了函数嵌套的形式,这样一来,输入是否合法就只用检查一次。而如果我们不使用函数嵌套,那么每调用一次递归便会检查一次,这是没有必要的,也会降低程序的运行效率。
实际工作中,如果你遇到相似的情况,输入检查不是很快,还会耗费一定的资源,那么运用函数的嵌套就十分必要了。
函数变量作用域
如果变量是在函数内部定义的,就称为局部变量,只在函数内部有效。
相对应的,全局变量则是定义在整个文件层次上的,比如下面这段代码:
MIN_VALUE = 1 MAX_VALUE = 10 def validation_check(value): if value < MIN_VALUE or value > MAX_VALUE: raise Exception('validation check fails')
这里的 MIN_VALUE 和 MAX_VALUE 就是全局变量,可以在文件内的任何地方被访问,当然在函数内部也是可以的。不过,我们不能在函数内部随意改变全局变量的值。比如,下面的写法就是错误的:
MIN_VALUE = 1 MAX_VALUE = 10 def validation_check(value): ... MIN_VALUE += 1 ... validation_check(5)
如果运行这段代码,程序便会报错:
UnboundLocalError: local variable 'MIN_VALUE' referenced before assignment
这是因为,Python 的解释器会默认函数内部的变量为局部变量,但是又发现局部变量 MIN_VALUE 并没有声明,因此就无法执行相关操作。所以,如果我们一定要在函数内部改变全局变量的值,就必须加上 global 这个声明:
MIN_VALUE = 1 MAX_VALUE = 10 def validation_check(value): global MIN_VALUE ... MIN_VALUE += 1 ... validation_check(5)
类似的,对于嵌套函数来说,内部函数可以访问外部函数定义的变量,但是无法修改,若要修改,必须加上 nonlocal 这个关键字:
def outer(): x = "local" def inner(): nonlocal x # nonlocal关键字表示这里的x就是外部函数outer定义的变量x x = 'nonlocal' print("inner:", x) inner() print("outer:", x) outer() # 输出 inner: nonlocal outer: nonlocal
闭包
闭包其实和刚刚讲的嵌套函数类似,不同的是,这里外部函数返回的是一个函数,而不是一个具体的值。返回的函数通常赋于一个变量,这个变量可以在后面被继续执行调用。
闭包也称为高阶函数
比如,我们想计算一个数的 n 次幂,用闭包可以写成下面的代码:
def nth_power(exponent): def exponent_of(base): return base ** exponent return exponent_of # 返回值是exponent_of函数 square = nth_power(2) # 计算一个数的平方 cube = nth_power(3) # 计算一个数的立方 square # 输出 <function __main__.nth_power.<locals>.exponent(base)> cube # 输出 <function __main__.nth_power.<locals>.exponent(base)> print(square(2)) # 计算2的平方 print(cube(2)) # 计算2的立方 # 输出 4 # 2^2 8 # 2^3
看到这里,你也许会思考,为什么要闭包呢?上面的程序,我也可以写成下面的形式啊!
def nth_power_rewrite(base, exponent): return base ** exponent
确实可以,不过,要知道,使用闭包的一个原因,是让程序变得更简洁易读。设想一下,比如你需要计算很多个数的平方,那么你觉得写成下面哪一种形式更好呢?
# 不适用闭包 res1 = nth_power_rewrite(base1, 2) res2 = nth_power_rewrite(base2, 2) res3 = nth_power_rewrite(base3, 2) ... # 使用闭包 square = nth_power(2) res1 = square(base1) res2 = square(base2) res3 = square(base3) ...
其次,和上面讲到的嵌套函数优点类似,函数开头需要做一些额外工作,而你又需要多次调用这个函数时,将那些额外工作的代码放在外部函数,就可以减少多次调用导致的不必要的开销,提高程序的运行效率。
另外还有一点,我们后面会讲到,闭包常常和装饰器(decorator)一起使用。