python函数由若干语句组成的语句块、函数名称、参数列表构成,它是组织代码的最小单元
函数的作用:
结构化编程对代码的最基本的封装,一般按照功能组织一段代码
封装的目的为了复用,减少冗余代码使代码更加简洁美观、可读易懂
函数定义
def 函数名(参数列表):
函数体(代码块)
[return 返回值]
- 函数名就是标识符,命名要求一样,语句块必须缩进,约定4个空格
- Python的函数若没有return语句,会隐式返回一个None值
- 定义中的参数列表称为形式参数,只是一种符号表达(标识符),简称形参
函数调用
函数定义,只是声明了一个函数,它不能被执行,需要调用执行
调用的方式,就是函数名后加上小括号,如有必要在括号内填写上参数。调用时写的参数是实际参数,是实实在在传入的值,简称实参。
函数参数
函数在定义是要约定好形式参数,调用时也提供足够的实际参数,一般来说,形参和实参个数要一致(可变参数除
外)。
传参方式有4种,分别是:
1.位置传参
定义时def f(x, y, z), 调用使用 f(1, 3, 5),按照参数定义顺序传入实参
2.关键字传参
用了形参的名字。传参时可以位置可以不一样。注:有位置参数和关键字传参的时候,位置参数要在关键字参数前。位置参数是按位置对应的
*args 可变位置参数
可以接受多个实际参数,将收集来的实参组织到一个tuple中
- **kwags 可变关键字参数,
将收集来的实参组成个字典 - 可变位置参数和可变关键字参数都可以收集若干个实参,可变位置参数收集形成一个tuple,可变关键字参数 收集形成一个dict,
def ad(x,y,z=4,*args,k=5,**kwargs):
print(x,y,z,k)
print(args)
print(kwargs)
参数解构
- 在给函数提供实参的时候,可以在可迭代对象前使用 * 或者 ** 来进行结构的解构,提取出其中所有元素作为 函数的实参
- 使用 *解构成位置传参
- 使用 ** 解构成关键字传参
- 提取出来的元素数目要和参数的要求匹配
参数解构只能在传实参的时候使用。
顺序为:普通参数,缺省值,可变位置参数,关键字参数,可变位置关键字参数
例子:
1、编写一个函数,能够接受至少2个参数,返回最小值和最大值
2、完成一个函数,可以接收输入的多个数,每一次都能返回到目前为止的最大值、最小值。
1>
def add(x,y,*args):
max_num = max(x,y,*args)
min_num = min(x,y,*args)
return max_num,min_num
import random
print(add(*[random.randint(10, 20) for i in range(random.randint(2, 10))]))
2>
def test():
max_ = min_ = None
while True:
num = input('>>>').replace(',',' ').split()
sums = [int(s) for s in num]
if num is None:
continue
if max_ is None:
max_=min_=sums[0]
max_=max(max_,*sums)
min_ = min(min_,*sums)
print(max_,min_)
test()
函数返回值
-
所有函数都有返回值,如果没有return语句,隐式调用return None
-
return 语句并不一定是函数的语句块的最后一条语句
-
一个函数可以存在多个return语句,但是只有一条可以被执行。如果没有一条return语句被执行到,隐式调用 return None
-
如果函数执行了return 语句,函数就会返回,当前被执行的return语句之后就不会被其他语句执行
-
返回值作用:结束函数调用,返回‘返回值’
-
函数不能同时返回多个值,如果返回多个值,隐式的被python封装成了一个元组
函数作用域***
作用域
一个标识符的可见范围,这就是标识符的作用域,一般常说的是变量的作用域
函数的封装会开辟一个作用域,变量如果被限制在这个作用域中,则外部函数变量不可见
注意: 每一个函数都会开辟一个作用域
作用域分类
- 全局作用域
-
- 在整个程序运行的环境中都可见
-
- 全局作用域中的变量称为全局变量
-
- 局部作用域
-
- 在函数、类等内部可见
-
- 局部作用域中的变量称为局部变量,其使用范围不能超过其所在的作用域
-
函数嵌套
global使用原则
外部作用域变量会在内部作用域可见,但也不要在这个内部的局部作用域中直接使用,因为函数的目的就是 为了封装,尽量与外界隔离
如果函数需要使用外部全局变量,请尽量使用函数的形参定义,并在调用传实参解决
不用global!
闭包
自由变量: 未在本地作用域中定义的变量。函数可以嵌套的,所以自由变量未必是全部变量
闭包:指的是内层函数引用到了外层函数的自由变量
如果要对这个普通变量使用闭包,Python3中可以使用nonlocal关键字
nonlocal 只能作用于当前作用域,不能用于全局变量,只用于嵌套内层函数
形参就是标识符,就是变量,就是本地变量
-- 全局变量
def counter():
global count
count = 0
def inc():
global count
count += 1
return count
return inc
foo = counter()
print(foo(), foo())
-- 用nonlocal使用局部变量
def counter():
count = 0
def inc():
nonlocal count
count += 1
return count
return inc
foo = counter()
print(foo(), foo())
递归Recursion
-
函数直接或者间接调用自身就是 递归
-
递归需要有边界条件,递归前进段、递归返回段
-
递归一定要有边界条件
-
当边界条件不满足的时候,递归前进
-
当边界条件满足的时候,递归返回
斐波那契数列:
def fib(n):
return 1 if n <= 1 else fib(n - 1) + fib(n - 2)
递归要求
- 递归一定要有退出条件,递归调用一定要执行到这个退出条件。没有退出条件的递归,就是无限调用
- 递归调用的深度不宜过深
-
- python对递归调用的深度做了限制,以保护解释器
-
- 超过递归深度限制,抛出recursionError:maxinum recursion depth exceedd超出最大深度
习题:
1、求n的阶乘
2、将一个数逆序放入列表中,例如1234-》[4,3,2,1]
3 、猴子吃桃问题
1. 阶乘
from functools import reduce
print(reduce(lambda x, y: x * y, range(1, 5)))
1. 递归:
def jc(n):
if n < 2:
return 1
return jc(n - 1) * n
print(jc(1))
2.
def petch(day=10):
if day == 1:
return 1
return 2 * (petch(day-1) +1)
print(petch())
3.
data = [1, 2, 3, 4]
def sor(data):
if not data:
return []
return [data[-1]] + sor(data[:-1])
print(sor(data))
或者列表做
def sor(data):
li = []
for i in range(len(data),0 , -1):
li.append(i)
return li
print(sor(data))
匿名函数
lambda函数
- 匿名函数:没有名字的函数。可以进行函数调用,传入的都是可调用对象,一般用于高阶函数,
- 如sorted格式为 lambda [参数列表]: 表达式
冒号后不能出现等号 - 参数列表不需要小括号。无参就不写参数
- 冒号用来分割参数列表和表达式部分 不需要使用return。表达式的值,就是匿名函数的返回值。表达式中不能出现等号 lambda表达式(匿名函数)只能写在一行上,也称为单行函数
如果太复杂则使用其他函数,
生成器
生成器generator
- 生成器指的是生成器对象,可以由生成器表达式得到,也可以使用yield关键字得到的一个生成器函数,调用这个函数得到一个生成器对象
- 生成器对象,是一个可迭代对象,是一个迭代器
- 生成器对象,是延迟计算,惰性求值
** 生成器函数 **
- 函数体中包含yield语句的函数,就是生成器函数,调用后返回生成器对象
m = (i for i in range(5)) # 生成器表达式
print(next(m))
--生成器函数
def inc()
for i in range(5):
yield i
g = inc()
for i in g:
print(i)
普通函数的调用,函数会立即执行直到执行完毕。
生成器函数调用,并不会立即执行函数体,而是需要使用next 函数来驱动生成器函数,执行后获得生成器对象,
生成器表达式和生成器函数都可以的到生成器对象,只不过生成器函数可以写的更加复杂的逻辑
生成器函数实现斐波那契数列:
def fibs(n):
a = 0
b = 1
for i in range(n):
a, b = b, a + b
return b
print(fibs(5))
--改造
def fibs():
a = 0
b = 1
while True:
yield b
a, b = b, a + b
fi = fibs()
for i in range(5):
print(next(fi))
yield from语法
yield from iterable 等价于 for item in iterable : yield item
yield from 是一种简化语法的语法糖
def inc():
for x in range(1000):
yield x
foo = inc()
print(next(foo))
print(next(foo))
# 改进 ---等价
def incs():
yield from range(1000)
fe = incs()
print(next(fe))
print(next(fe))
本质上yield from的意思就是,从from后面的可迭代对象中拿元素一个个yield出去。