书接上回,我们继续来学习有关函数的内容。
五、变量作用域
def test():
x = 10
y = 20
return x, y
x, y = test()
print(x)
print(y)
在上面这段代码中,我们定义了一个test函数,在函数中有两个变量x和y,在函数外我们使用两个变量来接收函数的返回值,变量名也叫x和y。在这里,函数内部的x和y,和函数外部的x和y,并不是相同的,只是恰好重名而已。
这段代码之所以能正常运行,是因为变量具有作用域。在函数内部定义的变量只在函数内部有效,函数调用结束之后,变量也会被销毁。
def test():
x = 10
y = 20
return x, y
test()
print(x)
print(y)
在这段代码中,虽然我们在函数外调用了test函数,但是函数的返回值并没有在函数外部被接收;也就是说,函数外部并没有名为x的变量,此时我们尝试打印变量x,程序就会抛出异常。(如下图)

在不同的作用域中,允许存在重名的变量:
def test():
x = 10
y = 20
return x, y
x, y = test()
print(x)
print(y)
依旧是上面这段代码,为了验证函数内部的x和y,与函数外部x和y,不是相同的,我们对代码进行处理,更直观的观察一下:
def test():
x = 10
y = 20
print(f'函数内部x={x}, y={y}')
return x, y
x, y = test()
print(f'函数外部x={x}, y={y}')
运行上面这段代码,就可以在控制台上清楚的观察到打印的x和y究竟是函数内部的还是函数外部的。

在函数内部的变量称之为局部变量,不在函数内部的变量称之为全局变量。
我们来看一段代码:
def test():
print(f'x = {x}')
x = 20
test()
在函数内部我们并没有定义x的值,而当我们调用函数时,函数能打印出全局变量x的值,这是因为:如果函数内部尝试访问的变量在局部作用域中不存在,那么就会尝试在全局作用域中寻找。
如果全局变量和局部变量同名,那么在函数内部会使用哪个变量呢?代码如下:
x = 10
def test():
x = 20
print(x)
test()
先在函数外部定义一个全局变量x,再在函数内部定义一个局部变量x,两个变量的值不同;运行上面这段代码,控制台输出的结果为 “20” 。由此可得出:在函数内部,当全局变量和局部变量同名时,局部变量优先。
如果想在函数内部,修改全局变量的值,有没有办法呢?
可以在函数内部使用 “global” 关键字声明要修改的变量:
x = 10
def test():
global x
x = 20
print(f'函数内部x = {x}')
test()
print(f'函数外部x = {x}')
在这段代码中,我们先在函数外部定义了一个全局变量x,在函数内部通过 “global” 关键字使用了全局变量x,再将全局变量x的值修改为 “20”。
正是因为 “global” 关键字,在函数内部的 “x = 20” 这条语句 是在对全局变量x进行赋值操作,而不是在函数内部创建一个名为x的局部变量。
在变量作用域这一知识点这里,我们还要特别注意:在Python中,if / while / for 等语句不会影响变量的作用域;换而言之,在 if / while / for 中定义的变量,在语句外也可以正常使用。
六、函数执行过程
在使用函数时,只有调用函数才会执行函数体内部的代码;当函数体执行结束或者遇到return语句,则会回到函数调用位置,继续往下执行。 我们通过代码来具体理解一下函数的执行过程:
def test():
print("test")
print("test2")
print('111')
test()
print('222')
test()
运行结果为:


七、链式调用
我们来看一段之前编写过的代码:
# 判断奇数
def is_odd(n):
if n % 2 != 0:
return True
else:
return False
result = is_odd(5)
print(result)
这段代码中,使用了result来接收函数调用后的返回值,再打印result。
最后两条语句其实可以合并为一条语句:
# 判断奇数
def is_odd(n):
if n % 2 != 0:
return True
else:
return False
print(is_odd(5))
像这样,将一个函数的返回值直接作为另一个函数的参数,这种操作就称为链式调用。 这也是一种很常见的写法。
八、嵌套调用
函数内部调用其他函数,就称为嵌套调用。
最简单的嵌套调用其实我们已经使用过了:
def test():
print("test")
print("test2")
在test函数中调用print函数。
我们再来看一段逻辑较为复杂的代码:
def a():
print('a')
def b():
print('b')
a()
def c():
print('c')
b()
def d():
print('d')
c()
d()
运行结果为:

函数的具体执行过程为:

函数之间的调用关系,在Python中可以使用一个特定的数据结构来表示,称为函数调用栈。 每次函数调用,都会在调用栈里新增一个元素,称为栈帧。 每个函数的局部变量,都包含在自己的栈帧中。
九、函数递归
函数递归是函数嵌套调用的一种特殊的情况,即自己调用自己。递归可以分成两个部分来看:递推和回归。
举个栗子:用函数递归的方式计算5的阶乘:
def test(n):
if n == 1:
return 1
return n * test(n - 1)
print(test(5))
解题思路:

具体执行过程为:

在使用递归函数的时候要注意:
1.务必存在递归结束条件。(在上面这个例子中,"if n == 1: " 就是递归结束条件。)
2.每次递归,函数的实参都应逐步趋近于函数结束条件。
若上述条件有一项没满足,就会出现无限递归的情况,这是一种典型的错误!程序也会抛出异常。
十、参数默认值
在Python中,可以给函数的形参指定默认值。带有默认值的参数,在调用的时候可以不传参。
举个栗子:计算两个数的和:
def add(x, y, debug = False):
if debug:
print(f'观察传递的参数的值:x = {x}, y = {y}')
return x + y
print(add(1, 2, True))
print('--------------------------')
print(add(1, 2))
运算结果为:

这就是一个带有参数默认值的函数,“debug = False” 即为参数默认值,当我们不指定第三个参数的时候,程序就会默认debug的值为False。
为了确保没有默认值的参数可以正常传递,带有默认值的参数需要放到没有默认值的参数的后面。
举个错误例子:
def add(x, debug = False, y):
if debug:
print(f'观察传递的参数的值:x = {x}, y = {y}')
return x + y
print(add(1, 2))
运行这段代码,程序就会报错:

十一、关键字参数
在调用函数时,需要给函数传递实参,一般情况下,默认都是按照形参的顺序依次传递实参。但是,也可以通过关键字参数 指定实参应该传递给哪个形参:
def test(x, y):
print(f'x = {x}')
print(f'y = {y}')
test(x = 10, y = 20)
test(y = 100, x = 200)
形如 “test(x = 10, y = 20)” 这种操作,即为关键字参数。
运行结果为:

在PyCharm中编写这段代码时,使用关键字参数,参数会变为红色(如下图),这并非错误,不必担心。

十二、函数小结
函数在编程语言中占据着必不可少的地位,在后续的学习中,我们也会广泛使用到函数。需多加理解、练习。
322

被折叠的 条评论
为什么被折叠?



