函数
函数是将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需要调用函数名即可
注:函数在没有执行前,内部代码不执行
特性:
减少重复的代码
使程序变得可扩展
使程序变得易维护
实例:
def hel(): # 定义函数名
print('Hello World!') # 函数内的内容
hel() # 调用函数
# 执行结果
Hello World!
函数的参数
- 形参变量
只有在被调用是才分配内存单元,形参只在函数内部有效,函数调用结束后返回主程序后则不在调用此形参变量
- 实参
可以是常亮,变量,表达式,函数等,无论实参是何种类型的变量,在进行函数调用时,它们都必须有特定的值,以便把这些值传给形参
实例:
def calc(x, y): # x,y 为形参
res = x**y
return res
s = calc(2, 5) # 2,5 是需要传送的实参数据
print(s)
# 执行结果
32
关键参数
实例:
def stu_register(name, age, country, course='CN'): #如传入的某个参数值是一样的,就可以在定义函数是指定参数值是多少,且指定的变量值的后面不允许有需要传入值的变量
print('注册学生信息'.center(10, '-'))
print('姓名:', name)
print('age:', age)
print('国籍:', course)
print('课程:', country)
stu_register('zevin', 22, 'python') #此处传入字符串需要严格对应参数内的变量
# 执行结果
--注册学生信息--
姓名: zevin
age: 22
国籍: CN
课程: python
# 也可以在传入参数值时指定变量
def stu_register(name, age, country, course):
print('注册学生信息'.center(10, '-'))
print('姓名:', name)
print('age:', age)
print('国籍:', course)
print('课程:', country)
stu_register('zevin',23, course='JP', country='python') #若没有指定变量的参数,需要按照函数格式传入值
# 执行结果
--注册学生信息--
姓名: zevin
age: 23
国籍:JP
课程: python
非固定参数
若函数中有些定义的变量需要传入多个参数,就可以使用非固定参数
用法:*变量, 在变量前加个 * 星号
实例:
# 变量前一个 * 星号
def stu_register(name, age, *args): # *args 会把多传入的值变成一个元祖形式
print(name, age, args)
stu_register('zevin', 22)
# 执行结果
# zevin 22 () #后面这个()就是args的值,因为没有传入值,所以为空
stu_register('zevin', 22, 'CN', 'JP')
# 执行结果
# zevin 22 ('CN', 'JP') # 最后多传入的值变成元祖
# 变量前两个 * 星号
def stu_register(name, age, *args, **kwargs): # **kwargs会把多传入的值变成一个字典形式
print(name, age, args, kwargs)
stu_register('zeivn', 22, 'CN', 'JP', sex='abc', pro=123)
# 执行结果
# zeivn 22 ('CN', 'JP') {'sex': 'abc', 'pro': 123}
函数的返回值
函数外部的代码想要获取函数执行的结果,就可以在函数内用 return 语句把结果返回,不加默认返回 None
注意:
函数在执行过程中只要遇到 return 语句,就会停止执行并返回结果
实例:
def sun(x, y):
if x > y:
x*y
else:
x**y
fo = sun(5, 6)
print(fo)
# 执行结果为 None
def sun(x, y):
if x > y:
return x*y
else:
return x**y
fo = sun(5, 6)
print(fo)
# 执行结果为 15625
局部变量
在函数中定义的变量称为局部变量,在程序一开始定义的变量称为全局变量
当全局变量和局部变量同名时,在定义局部变量的函数内,局部变量起作用,在其他地方全局变量起作用
实例:
name = 'anv' # 在程序开始定义 name = anv
def change_name():
name = 'dsf' # 在函数内定义 name = dsf
print('函数内name为:', name, id(name)) #在函数内打印name
change_name()
print('函数外name为:', name, id(name)) #在函数外打印name
# 执行结果
函数内name为: dsf 140357042555120
函数外name为: anv 140357042555064
# 要想在函数内修改全局变量,需要用参数 global 修改
name = 'anv' # 在程序开始定义 name = anv
def change_name():
global name # global声明需要修改的变量,建议不要更改全局变量
name = 'dsf' # 在函数内修改为 name = dsf
print('函数内name为:', name, id(name)) #在函数内打印name
change_name()
print('函数外name为:', name, id(name)) #在函数外打印name
# 执行结果
函数内name为: dsf 140056539031792
函数外name为: dsf 140056539031792
嵌套函数
函数内部可以再次定义函数,执行需要调用
# 实例 1
age = 18 # 全局定义 age = 18
def func1():
age = 73 # 函数内定义 age = 73
print('func1:', age)
def func2():
age = 84
print('func2:', age)
func1() # 调用 func1()
# 执行结果,未调用 func2() 所有以输出 func1 的值
func1:73
# 实例 2
age = 18 # 全局定义 age = 18
def func1():
age = 73 # 函数内定义 age = 73
print('func1:', age)
def func2():
age = 84
print('func2:', age)
func2() # 调用 func2()
func1() # 调用 func1()
# 执行结果
func1:73
func2:84
# 实例 3
age = 18 # 全局定义 age = 18
def func1(): # 函数内定义 age = 73
print('func1:', age) # 此age 直接调用的是全局变量 age 值
def func2():
print('func2:', age)
func2() # 调用 func2()
func1() # 调用 func1()
# 执行结果
func1: 18
func2: 18
# 实例 4
age = 18 # 全局定义 age = 18
def func1(): # 函数内定义 age = 73
age = 73 # 函数内定义 age = 73
print('func1:', age)
def func2():
print('func2:', age) # func2()函数内的age从上一层父级获取 age 值,如果上一层 age未赋值,则在往上一层查找
func2() # 调用 func2()
func1() # 调用 func1()
# 执行结果
func1: 73
func2: 73
匿名函数
把多行语句提供成一行,不需要函数名,支持三元运算,主要是和其他方式搭配运算
# 如以下代码
def calc(x, y):
return x*y
print(calc(2, 5))
# 执行结果
10
# 换成匿名函数
calc = lambda x, y: x*y
print(calc(2, 5))
# 执行结果
10
# 还可以进行三元运算
calc = lambda x, y: x*y if x < y else x/y
print(calc(16, 8))
# 执行结果
2.0
# 匿名函数主要搭配其他函数使用
# map(func, list) 是将后面列表中的每一个值交给前面的函数去执行一边
res = map(lambda x: x*2, [1, 5, 6, 7]) #将后面的列表内每一个值交给前面的 lamba 函数进行自乘
for line in res:
print(line)
# 执行结果
2
10
12
14
高阶函数
变量可以指向函数,函数的参数能接受变量,那么一个函数可以接受另一个函数作为参数,这种就是 高阶函数
- 接受一个或多个函数作为输入
- return 返回另外一个函数
def calc(x, y):
return x*y
def func(x):
return x+x
print(calc(func(4), 5)) # 将func(4)返回的函数结果作为calc函数的参数
# 执行结果
40
作用域
Python 中作用域,变量会由内向外查找,先在自己作用域查找,如果没有就再去上级查找,依次向上,直到找不到就报错
# 先看这段代码
name = 'li'
def f1():
print(name)
def f2():
name = 'al'
return f1
res = f2()
res()
# 输出:li
# 分析上面的代码,先执行f2()函数,而f2()执行结果返回的 f1 的内存地址,所以res=f1,在后面执行res()就等于执行f1(),而执行f1()时和执行f2()没有关系,f1()内并没有定义变量name的值,所以f1()就上级查找name变量的值,所有 name = "li" 就是和 f1()为一个作用域链,最后返回的结果为 li
# 最后看这段代码
name = 'li'
def f1():
print(name)
def f2():
name = 'al'
f1()
f2()
# 输出: li
# 此段代码和上面的基本是一样的,执行f2()后调用了f1(),最后的结果为f1()向上级查找name变量值,最后返回name变量值
# 在函数未执行之前,作用域已经形成了,而作用域链也生成了
作用域的查找顺序:
LEGB:
- locals: 函数内部的名称空间,包括局部变量和形参
- enclosing: 外部嵌套函数的名称空间
- globals: 全局变量,函数定义所在模块的名称空间
- builtins 内置模块的名称空间
递归
在函数内部可以调用其他函数,如果一个函数在内部调用自己本身时,这个函数就一个递归函数
递归特性:
- 必须要有一个明确的结束条件
- 每次进入更深一层递归时,问题规模应该比上级递归减少
- 递归的效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。)
# 递归的用法
# 计算 介乘,
def num(n):
if n == 1:
return 1
return n * num(n - 1)
print(num(5))
# 输出:120