1. 实参的类型
- 实参可以传递任意类型的对象;
def fn(a):
print('a = ', a)
print('传递整数参数: ', end='')
b = 123
fn(b)
print('传递字符串参数: ', end='')
b = 'hello'
fn(b)
运行结果:
- 调用函数的时候,解析器不会检查实参的类型;
def fn(a, b):
print(f'{a} + {b} = {a + b}')
# 传递了一个整数和字符串参数
fn(1, '2')
# 错误信息:
TypeError: unsupported operand type(s) for +: 'int' and 'str'
运行结果:
解析器不检查实参类型,编写代码时不会发出警告。
- 在函数中重新给形参赋值,不会影响传递的变量;
def fn(a):
# 在函数中给形参赋值
a = 10
print('a = ', a, ', id(a) = ', id(a))
c = 1
fn(c)
print('c = ', c, ', id(c) = ', id(c))
运行结果:
使用 = 赋值运算符会改变变量指向的对象,上述代码中函数中的变量a和c已经指向不同的对象。
- 如果现在的形参执行的是可变对象,当我们通过形参修改对象时,会影响到所有指向该变量的对象
def fn(a):
# 形参a传递列表,修改列表中的某个元素
a[0] = 10
print('a = ', a, ', id(a) = ', id(a))
c = [1, 2, 3]
fn(c)
print('c = ', c, ', id(c) = ', id(c))
运行结果:
对于上述结果,笔者的理解是参数传递时实参将会赋值给对应的形参,此时a和c指向同一个对象即列表[1,2,3],而使用下标修改列表元素,不会改变变量指向的对象,列表作为可变对象,列表元素修改后所有指向它的变量都会受影响。
如果不希望函数中改变a会影响c,就可以之前课程中提及的copy方法。
# fn(c)
fn(c.copy())
运行结果:
2. 不定长参数
2.1 带*参数的使用
- 在定义函数的时候,可以在形参之前加上*号,这个形参可以获取到所有的实参,它会将获取到的实参放到一个元祖当中;
def fn(*a):
print('a = ', a, type(a))
print('不传递实参:', end='')
fn()
print('传递任意数量实参:', end='')
fn('hello', 1, 2.0, True, [1, 2, 3])
运行结果:
*args 会接受所有位置实参,并且会统一保存到一个元祖当中。
小练习:求任意数量的参数之和。
def mysum(*args):
# 定义变量保存结果
a = 0
# 遍历元祖,将元祖中的数进行相加
for i in args:
a += i
print('result = ', a)
mysum(1, 2, 3, 4)
运行结果:
- 带*号的形参只能有一个;
- 不定长参数不一定非要写最后,但是带*参数后面的参数,都必须以关键字参数的方式进行传递;
def fn1(a, b, *c):
print('a = ', a)
print('b = ', b)
print('c = ', c)
print('带*形参放在最后:')
fn1(1, 2, 3, 4, 5)
def fn2(a, *b, c):
print('a = ', a)
print('b = ', b)
print('c = ', c)
print('带*形参放在中间,后续不用关键字传参:')
fn2(1, 2, 3, 4, 5)
运行结果:
def fn2(a, *b, c):
print('a = ', a)
print('b = ', b)
print('c = ', c)
print('带*形参放在中间,后续为关键字传参:')
fn2(1, 2, 3, 4, c = 5)
运行结果:
- 带*参数没有写在最后,若之后的参数没有使用关键字传参且有默认值的情况下,也不会报错,那么该参数将使用默认值,传递的参数去除原本的位置参数外,都将打包给*参数。
def fn2(a, *b, c=5):
print('a = ', a)
print('b = ', b)
print('c = ', c)
print('带*形参放在中间,后续形参有默认值:')
fn2(1, 2, 3, 4)
运行结果:
从上述代码运行的结果看:* 形参只能接收位置参数。
2.2 带**参数的使用
-
** 形参可以接收其它关键字参数,它会将这些参数统一保存到一个字典当中。
在该字典中,key就是参数的名字,字典的value就是参数值。 -
** 形参只能有一个,并且只能写在最后面.
def fn3(a, b, **c):
print('a = ', a)
print('b = ', b)
print('c = ', c)
print('带**形参的使用:')
fn3(d = 3, e = 4, a = 1, b = 2)
运行结果:
a和b使用关键字传参时可以不用按照其原本的位置,剩余参数都必须以关键字传参的方式给c,并且被转换为字典使用。
从上述代码运行的结果看:** 形参处理关键字参数。
3. 函数的返回值
- 返回值就是函数执行以后返回的结果,可以通过return来指定函数的返回值;
- 可以直接使用函数的返回值,也可以通过一个变量来接收函数的返回值;
def fn1():
return 123
def fn2():
return 'hello'
def fn3():
return [1, 2, 3, 4]
def fn4():
return {'name': 'John', 'age': 10}
# 通过变量接收函数返回值
r = fn1()
print('返回整数:', r)
r = fn2()
print('返回字符串:', r)
r = fn3()
print('返回列表:', r)
r = fn4()
print('返回字典:', r)
运行结果:
- return 可以接任何对象
函数没有写return,或者只写了return,那么相当于return None
def fn1():
print('This is fn1', end=' ')
return None
def fn2():
print('This is fn2', end=' ')
def fn3():
print('This is fn3', end=' ')
return
r = fn1()
print('返回值:', r)
r = fn2()
print('返回值:', r)
r = fn3()
print('返回值:', r)
运行结果:
- 在函数中,return 后面的代码不会执行,return一旦执行,函数自动结束。
def fn3():
for i in range(5):
if i == 3:
break
print('i = ', i)
print('循环执行完毕')
fn3()
上述代码的执行结果如下:
如果在语句print('循环执行完毕')
前加上return,执行结果如下:
4. 函数的作用域
作用域就是变量生效的区域。
- 全局作用域
全局作用域在程序执行时创建,在程序执行结束时销毁。
所有函数以外的区域都是全局作用域。
在全局作用域中定义的变量,都是全局变量,全局变量可以在程序的任意位置进行访问。 - 函数作用域
函数作用域在函数调用时创建,在调用结束时销毁。
函数每调用⼀次就会产生⼀个新的函数作用域。
在函数作用域中定义的变量,都是局部变量,它只能在函数内部被访问。
b = 20
def fn():
a = 10
print('函数内部, a = ', a)
print('函数内部,b = ', b)
fn()
print('函数外部,b = ', b)
print('函数外部, a = ', a)
运行结果:
- 如果希望在函数内部修改全局变量的指向,则需要使用global关键字,声明变量。
a = 10
def fn():
a = 20
print('函数内部, a = ', a)
fn()
print('函数外部, a = ', a)
运行结果:
a = 10
def fn():
global a # 使用global关键字声明a
a = 20
print('函数内部, a = ', a)
fn()
print('函数外部, a = ', a)
运行结果:
变量a被global声明后,表示全局变量。函数内部对其重新赋值后,函数外部访问该变量时已经是修改后的值。
5. 递归函数
递归是解决问题的⼀种方式,它和循环很像。
递归式函数就是在函数中自己调用自己。
递归的整体思想是:将⼀个大问题分解为⼀个个的小问题,直到问题无法分解时,再去解决问题。
递归式函数有2个条件:
- 基线条件
问题可以被分解为最小问题,当满足基线条件时,递归就不再执行。 - 递归条件
可以将问题继续分解的条件。
代码样例:
# 创建一个函数,用递归的方式
def fn(n):
# 基线条件
if n == 1:
return 1
# 递归条件
return n * fn(n-1)
n = 10
r = fn(n)
print(f'{n}的阶乘为:', r)
运行结果:
# 创建一个函数,为任意数字做任意幂运算
def fn1(n, i):
# 基线条件
if i == 1:
return n
# 递归条件
return n * fn1(n, i-1)
n = 5
i = 6
r = fn1(n, i)
print(f'{n}的{i}次幂为:', r)
运行结果: