Python函数

566 阅读3分钟

什么是函数?

我们都知道,函数就是为了实现某一功能的代码段,只要写好后,就可以重复利用。

def name(param1,param2,...,param3):
	statements
	return/yield value # optional

和其他需要编译的语言不一样的是,def是可执行语句,这意味着函数直到被调用前,都是不存在的。但主程序调用函数时,必须保证这个函数此前已经定义过。

设置默认值

Python的函数可以设定默认值,如:

def func(param = 0):
	...

这样,在调用函数func()时,如果参数param没有传入,则参数默认为0;而如果传入了参数param,其就会覆盖默认值。

多态

Python和其他语言相比的一大特点是,Python是dynamically typed的,可以接受任何数据类型(整型,浮点,字符串等)。对函数来说,这一点同样适用。

所以Python不用考虑输入的数据类型,而是将其交给具体的代码去判断执行。在编程语言中,我们把这种行为称为多态。当然,Python这种方便的特性,在实际使用中也会带来诸多问题。因此,数据的类型检查很有必要

嵌套

Python函数的另一大特性,是Python支持函数的嵌套。所谓的函数嵌套,就是指函数里面又有函数,如:

def adder(x,z):
	def wrapper(y):
		return x + y + z
	return wrapper

函数的嵌套,主要有下面两个方面的作用。

  1. 函数的嵌套能保证内部函数的隐私。内部函数只能被外部函数所调用和访问,不会暴露在全局作用域,因此,如果你的函数内部有一些隐私数据(比如数据库的用户、密码等),不想暴露在外,那你就可以使用函数的的嵌套,将其封装在内部函数中,只通过外部函数来访问。比如:

def connect_DB():
    def get_DB_configuration():
        ...
        return host, username, password
    conn = connector.connect(get_DB_configuration())
    return conn

这里的函数get_DB_configuration,便是内部函数,它无法在connect_DB()函数以外被单独调用。我们只能通过调用外部函数connect_DB()来访问它,这样一来,程序的安全性便有了很大的提高。

  1. 合理的使用函数嵌套,能够提高程序的运行效率。

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))

以上函数使用嵌套就可以达到输入是否合法只用检查一次。如果不是嵌套函数,就会在每次调用递归都会去检查一次,会降低程序的运行效率。

函数变量的作用域

如果变量是在函数内部定义的,被称为局部变量,只在函数内部有效。一旦函数执行完毕,局部变量就会被回收,无法访问。

相对应的,全局变量则是定义在整个文件层次上的。可以在文件内的任何地方被访问。不过,我们不能在函数内部随意改变全局变量的值

如果一定要在函数内部改变全局变量的值,就必须加上global这个声明。

MIN_VALUE = 1
MAX_VALUE = 10
def validation_check(value):
    global MIN_VALUE
    ...
    MIN_VALUE += 1
    ...
validation_check(5)

这里的 global 关键字,并不表示重新创建了一个全局变量 MIN_VALUE,而是告诉 Python 解释器,函数内部的变量 MIN_VALUE,就是之前定义的全局变量,并不是新的全局变量,也不是局部变量。这样,程序就可以在函数内部访问全局变量,并修改它的值了。

类似的,对于嵌套函数来说,内部函数可以访问外部函数定义的变量,但是无法修改,若要修改,必须加上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

使用闭包的一个原因,是让程序变得更简洁易读。另一个是函数开头需要做一些额外工作,而你又需要多次调用这个函数时,将那些额外工作的代码放在外部函数,就可以减少多次调用导致的不必要的开销,提高程序的运行效率。

为什么要使用闭包

闭包,顾名思义,就是一个封闭的包裹,里面包裹着自由变量,就像在类里面定义的属性值一样,自由变量的课件范围随同包裹,哪里可以访问到这个包裹,哪里就可以访问到这个自由变量。

一般来说,当对象中只有一个方法时,这时使用闭包是更好的选择。

这比用类来实现更优雅,此外装饰器也是基于闭包的一中应用场景。

所有函数都有一个 __closure__属性,如果这个函数是一个闭包的话,那么它返回的是一个由 cell 对象 组成的元组对象。cell 对象的cell_contents 属性就是闭包中的自由变量。

def adder(x,z):
	def wrapper(y):
		return x + y + z
	return wrapper

adder5 = adder(5, 8)
print(adder5(10)) #23
print(adder5(11)) #24
print(adder5) #<function adder.<locals>.wrapper at 0x102bf3840>
print(adder5.__closure__) #(<cell at 0x102bb04f8: int object at 0x10096ca00>, <cell at 0x102bb05b8: int object at 0x10096ca60>)
print(adder5.__closure__[1].cell_contents) #8