python - 函数
---恢复内容开始---
1. 函数
(1) 如果想定义一个什么事都不做的空函数, 可以用 pass 语句, pass语句可以用来作为占位符, 比如现在还没有想好怎么蝎函数代码, 就可以先放一个pass, 让代码能运行起来
def nop(): pass
(2) 调用函数时 ,如果参数个数不对, python解释器会自动检查出来, 并抛出TypeError, 但是如果参数类型不对, python就无法帮我们检查. 因此可以根据需要适当添加参数检查, 添加了参数检查后, 如果传入错误的参数类型, 函数就可以抛出一个错误
def my_abs(x): if not isinstance(x, (int, float)): raise TypeError('bad operand type') if x >= 0: return x else: return -x
(3) 函数可以返回多个值, 但其实只是一种假象, python函数返回的其实是一个tuple. 但是在语法上, 返回一个tuple可以省略括号, 而多个变量可以同时接收一个tuple, 按位置赋予对应的值, 所以, python的函数返回多值其实就是返回一个tuple, 但是写起来更方便
(4) 函数执行完毕也没有return语句时, 自动return None
(5) 练习: 定义一个函数quadratic(a,b,c), 接收3个参数, 返回一元二次方程ax^2 + bx +c = 0的两个解, 计算平方根可以调用math.sqrt() 函数:
import math def quadratic(a, b, c): if a == 0: return -b/c tmp = b**2 - 4*a*c if tmp < 0: print('无解') return else: x1 = (-b + math.sqrt(tmp)) / (2 * a) x2 = (-b - math.sqrt(tmp)) / (2 * a) return x1,x2 print(quadratic(2, 3, 1)) print(quadratic(1, 3, -4))
2. 函数的参数
(1) 位置参数: 必须传入的参数
#计算x的平方的函数 位置参数 def power(x): return x * x
(2) 默认参数 : 默认参数必须指向不变对象str None这样的
#位置参数 x的n次方 def power(x, n=2): s = 1 while n > 0: n = n - 1 s = s * x return s
(3) 可变参数 : 传入的参数个数是可变的, 可以是1个, 2个 n个. 定义可变参数和定义一个list或tuple参数相比,仅仅在参数前面加了一个 * 号. 这些可变参数在函数调用时自动组装为一个tuple
def calc(*numbers): sum = 0 for n in numbers: sum = sum + n * n return sum print(calc(1, 2)) print(calc(1, 2 , 3))
在函数内部, 参数numbers接收到的是一个tuple
如果已经有了一个list或者tuple,要调用一个可变参数, python允许在list或tuple前面加一个*号, 把list或tuple的元素变成可变参数传进去:
>>> nums = [1, 2, 3] >>> calc(*nums) 14
(4) 关键字参数 : 关键字参数在函数调用时, 这些关键字参数在函数内部自动组装为一个dict
def person(name, age, **kw): print('name:', name, 'age:', age, 'other:', kw)
关键字参数可以扩展函数的功能. 比如, 在person函数里, 我们保证能接收到name和age这两个参数, 但是, 如果调用者愿意提供更多的参数, 我们也能收到.
和可变参数类似, 也可以组装出一个dict, 然后把该dict转换为关键字参数传进去
>>> extra = {'city': 'Beijing', 'job': 'Engineer'} >>> person('Jack', 24, **extra) name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
注意: 传进去的参数, 函数内部获得是一份拷贝!!!!
(5) 命名关键字参数 : 对于关键字参数, 如果要要限制传入的关键字参数的名字, 可以用命名关键字参数
def person(name, age, *, city, job): print(name, age, city, job)
和关键字参数**kw不同, 命名关键字参数需要一个特殊分隔符*, *后面的参数被视为命名关键字参数
如果函数定义中已经有了一个可变参数, 后面跟着的命名关键字参数就不在需要一个特殊分隔符*了
def person(name, age, *args, city, job):
print(name, age, args, city, job)
命名关键字参数必须传入参数名, 这个位置参数不同, 如果没有传入参数名, 调用将报错
(6) 参数组合
在python中定义函数, 可以用必须参数, 默认参数, 关键字参数和命名关键字采纳数, 这5中参数都可以组合使用, 但是请注意, 参数定义的顺序必须是 : 必选参数, 默认参数, 可变参数, 命名关键字参数, 关键字参数
3. 递归函数
(1) 使用递归函数需要注意防止栈溢出, 在计算机中, 函数调用是通过栈这种数据结构实现, 每当进入一个函数调用, 栈就会加一层栈帧, 每当函数返回, 栈就会减一层栈帧. 由于栈的大小不是无限的, 所以, 递归调用的次数过多, 会导致栈溢出.
(2) 解决递归调用栈溢出的方法是通过尾递归优化.
(3) 尾递归是指, 在函数返回的时候, 调用自身本身, 并且, return语句不能包含表达式, 这样编译器或者解释器就可以把尾递归做优化吗使递归本身无论调用多少次, 都只占用一个栈帧, 不会出现栈溢出的情况
(4) 目前大多数编程语言没有针对尾递归做优化, python解释器也没有做优化.
# 利用递归函数移动汉诺塔: def move(n, a, b, c): if n == 1: print('move', a, '-->', c) return move(n-1, a, c, b) print('move', a, '-->', c) move(n-1, b, a, c) move(4, 'A', 'B', 'C')