07 python基础六--函数

1 调用函数

所谓调用函数,即指调用python提供的内置函数,需要知道的是,要调用一个函数,必须知道它的名称和参数
Python内置函数列表:https://docs.python.org/3/library/functions.html

#当然除了查看文档外,python还支持help()查看函数信息的方式
#eg
>>>help(abs)
Help on built-in function abs in module __builtin__:

abs(...)
    abs(number) -> number
    
    Return the absolute value of the argument. #返回一个数字的绝对值

>>> abs(100)
100
>>> abs(-100)
100

# 最大值
>>> max(1,2)
2
>>> max(1,2,3,4,5)
5

# 调用内置函数进行数据类型的转换
>>> int('123')
123
>>> int(12.34)
12
>>> float('12.34')
12.34
>>> str(123)
'123'
>>> bool(1)
True
>>> bool('')
False
>>> bool(0)
False

函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”:

>>> a = abs
>>> a(-123)
123

2 定义函数

  • 当内置函数无法满足我们的需求时,可以自定义一个函数
  • 在Python中,定义一个函数使用def语句,依次写出函数名、括号、参数和冒号,然后,在缩进块中编写函数体,函数的返回值用return语句返回
>>> def ma_abs(x):
...     if not instance(x, (int, float)):
...         raise TypeError('bad operand type')
...     if x >= 0:
...         return x+x
...     else:
...         return x-x
... 
>>> ma_abs(-2)
0
>>> ma_abs(2)
4
  • 空函数,什么事也不做的空函数可以用pass语句
  • 实际上,pass一般用来作为占位符,比如现在没想好怎么写函数的代码,可以放一个pass让代码先运行起来
>>> def nop():
...     pass
... 
>>> nop()
>>> 

  • Python函数可以返回多个值?
  • 举个栗子🌰,比如在游戏中经常需要从一个点移动到另一个点,给出坐标、位移和角度,就可以计算出新的新的坐标:
>>> import math #导入math包
>>> def move(x, y, step, angle = 0):
...     nx = x + step * math.cos(angle)
...     ny = y - step * math.sin(angle)
...     return nx, ny
... 
>>> x, y = move(100, 100, 60, math.pi / 6)
>>> print(x, y)
(151.96152422706632, 70.0)  # 通过结果可见:Python函数返回的其实仍然是单一值,是一个tuple,语法上,tuple可以省略括号。多个变量按位置赋值tuple的对应值

3 函数的参数

Python的函数定义非常简单,但灵活度却非常大。除了正常定义的必选参数外,还可以使用默认参数、可变参数和关键字参数。使得函数定义出来的接口,不但能处理复杂的参数,还可以简化调用者的代码。

  • 位置参数、默认参数
>>> def power(x):
...     return x * x
... 
>>> power(2)
4
>>> power(6)
36

# x,n 即为函数的位置参数,传入的两个值按照位置顺序依次赋给参数x和n,n可以赋予默认值,称为默认参数
>>> def powerPlus(x, n = 2):
...     s = 1
...     while n > 0:
...         n = n - 1
...         s = s * x
...     return s
... 
>>> powerPlus(2, 3)
8
>>> powerPlus(4, 4)
256
  • 可变参数
    举个栗子🌰:以数学题为例子,给定一组数字a,b,c……,请计算a2 + b2 + c2 + ……
>>> def calc(numbers):
...     sum = 0
...     for n in numbers:
...         sum = sum + n * n
...     return sum
... 
# 普通的参数只能接受一个值,要想实现多值,需要先组装出一个list或tuple
>>> calc([1,2,3])
14
>>> calc((1,2,3,4))
30

# 可变函数出场!!!
>>> def calc(*numbers):
...     sum = 0
...     for n in numbers:
...         sum = sum + n * n
...     return sum
... 
>>> calc(1,2,3,4)
30
>>> calc()
0

        定义可变参数和定义一个list或tuple参数相比,仅仅在参数前面加了一个*号。在函数内部,参数numbers接收到的是一个tuple,因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数

  • 关键字参数

        可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。请看示例:

>>> def person(name, age, **kw):
...     print('name:', name, 'age:', age, 'other:', kw) 
... 
# 调用该函数外,可以只传入必选参数
>>> person('Bob', 30)
('name:', 'Bob', 'age:', 30, 'other:', {})
# 也可以传入任意个数的关键字参数
>>> person('Ami', 18, city = 'beijing')
('name:', 'Ami', 'age:', 18, 'other:', {'city': 'beijing'})
>>> person('Hua', 20, gender = 'M', Phone = 188011102222)
('name:', 'Hua', 'age:', 20, 'other:', {'gender': 'M', 'Phone': 188011102222})
# 简化调用方式
>>> extra = {'city':'beijing', 'job':'Teacher', 'Phone':13366667777}
>>> person('Lw', 26, **extra)
('name:', 'Lw', 'age:', 26, 'other:', {'city': 'beijing', 'job': 'Teacher', 'Phone': 13366667777})
  • 命名关键字参数
    对于关键字参数,函数的调用者可以传入任意不受限制的关键字参数。至于到底传入了哪些,就需要在函数内部通过kw检查。
    如果要限制关键字参数的名字,就可以用命名关键字参数,例如:只接收city和job作为关键字参数
>>> def person(name, age, **kw):
...     if 'city' in kw:
...         pass
...     if 'job' in kw:
...         pass
...     print('name:', name, 'age:', age, 'other:', kw)
... 
# 调用者传入参数个数不受限制
>>> person('Jack', 24, city='beijing', addr='chaoyang', job='coder')
name: Jack age: 24 other: {'city': 'beijing', 'addr': 'chaoyang', 'job': 'coder'}
# 采用**命名关键字参数**来限制参数个数,倘若多传参数进去则会报错
>>> def person(name, age, *, city, job):
...     print(name, age, city, job)

>>> person('Jack', 22, city='beijing', job='coder')
Jack 22 beijing coder

# 如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了:
>>> def person(name, age, *args, city, job):
...     print(name, age, args, city, job)
... 
>>> 
>>> person('Bob', 23, 1, 2, city = 'taiyuan', job = 'coder' )
Bob 23 (1, 2) taiyuan coder

在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数

4 递归函数

在函数内部,可以调用其他函数。如果一个函数在内部调用本身,这个函数就是递归函数。
举个栗子🌰:
计算阶层 n! = 1 * 2 * 3 * 4 * 5 … * n,用函数fact(n)表示:
fact(n) = n! = 1* 2 * 3 * … * (n-1) * n = fact(n-1) * n
所以,fact(n) 可以表示为 n * fact(n-1),只有n = 1时需要特殊处理

>>> def fact(n):
...     if n == 1:
...         return 1
...     return n * fact(n - 1)
... 
>>> fact(1)
1
>>> fact(5)
120

# 溢出
>>> faxt(1000)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'faxt' is not defined

递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
使用递归函数需要注意防止栈溢出,在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,就会减一层栈帧。由于栈的大小不是无限的,所以递归调用的次数过多,会导致栈溢出。
解决递归调用栈溢出的方法是通过尾递归优化

尾递归优化是指:在函数返回的时候,调用函数本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身不论调用多少次,都只占用一个栈帧,不会出现溢出的情况。

>>> def fact(n):
...     return fact_iter(n, 1)
... 
>>> def fact_iter(num, product):
...     if num == 1:
...         return product
...     return fact_iter(num - 1, num * product)
... 
>>> fact(2)
2
fact(100)
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000L

尾递归调用时,如果做了优化,栈不会增长,因此,无论多少次调用也不会导致栈溢出。

遗憾的是,大多数编程语言没有针对尾递归做优化,Python解释器也没有做优化,所以,即使把上面的fact(n)函数改成尾递归方式,也会导致栈溢出。

    原文作者:祐吢房_2c9a
    原文地址: https://www.jianshu.com/p/c447dd5e51b0
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞