函数基础和函数参数
一、函数基础
1.函数定义
def 函数名(参数):
'''
函数接口
'''
函数体
return 返回值
-
def:定义函数关键字
-
函数名命名规则: 字母、数字和下划线组成,和变量命名规则一致
-
参数:传入的参数必须放在圆括号中间,用逗号分隔
-
函数接口:此函数功能的注释
-
return :返回值关键字,后面可以返回任意表达式,但不能是赋值语句
-
返回值 : 待返回的值,默认返回None
2.函数调用
函数名(参数)
3.return
注意 return 和 print 的区别,return是函数的返回值,返回值可以赋值给变量,而print只是打印出来
return语句用于表示函数执行到此结束,并且返回return后面的对象,一旦函数执行过程遇到return语句,那么之后函数体内的所有代码都会被忽略,直接跳出函数体,哪怕正在一个循环内
二、函数参数
1.参数
(1).位置参数
位置参数也叫必传参数,顺序参数,按照位置关系,一一对应
def my_sum(a, b, c):
res = a + b + c
return res
sum = my_sum(1, 2, 3)
print(sum) # 6
a, b, c: 形式参数,简称形参
1, 2, 3: 实际参数,简称实参
(2).默认参数
默认参数必须写在位置参数的后面
def my_sum(a, b, c=666):
res = a + b + c
return res
sum = my_sum(1, 2)
print(sum) # 669
c=666: 默认参数,调用的时候如果没有传入参数,就用默认值,有实参就覆盖
(3).关键字参数
my_sum(c = 1, a = 2, b = 3): 关键字参数,函数参数会按照关键字去找对应的值,不需要位置一一对应
(4).动态参数
动态参数,必须放在所有的位置参数和默认参数后面,*args
必须出现在**kwargs
之前
def my_sum(*args, **kwargs):
res = args
kes = kwargs
return res, kes
sum = my_sum(1, 2, b = 23, c = 43)
print(sum) # ((1, 2), {'b': 23, 'c': 43})
动态参数:函数接受参数的个数是动态的,关键是一个和两个星号的区别
一般写法:def f(*args, **kwargs)
*args
: 所有多余的位置参数都会被args
接收生成一个元组
**kwargs
: 所有的关键字形参都会被kwargs
接收生成一个字典
2.拓展
(1).为动态参数传入列表、元组
def f(*args):
print(args, type(args))
L = [1, 2, 3]
# 把整个列表当做一个元素传进去
f(L) # ([1, 2, 3],) <class 'tuple'>
f(L, 4) # ([1, 2, 3], 4) <class 'tuple'>
# 把列表内部元素逐一作为参数传进去
f(*L, 4) # (1, 2, 3, 4) <class 'tuple'>
(2).为动态参数传入字典
def f(**kwargs):
print(kwargs, type(kwargs))
D = {"a" : 1, "b" : 2}
# 把字典内部元素逐一作为参数传进去,不加**报错
f(**D) # {'a': 1, 'b': 2} <class 'dict'>
(3).形参位置规定关键字参数
关键字参数前面需要一个特殊分隔符*
和位置参数及默认参数分隔开来,*
后面的参数被视为关键字参数
在函数调用时,关键字参数必须传入参数名,如果没有传入参数名,调用将报错
def student(name, age, *, sex):
pass
student("zzh", 18, sex='male')
如果函数定义中已经有了*args
参数,后面跟着的命名关键字参数就不再需要特殊分隔符*
了
def student(name, age=10, *args, sex):
pass
student(name="zzh", age=18, sex='male')
关键字参数可以设置默认值,想要修改默认值,必须传入参数名
def func(a, b, *args, c=1):
print(a, b, c) # 1 2 3
print(args) # ('q', 'w', 'e')
lis = ["q", "w", "e"]
func(1, 2, *lis, c=3)
否则,它将被视为*args
的参数
def func(a, b, *args, c=1):
print(a, b, c) # 1 2 1
print(args) # ('q', 'w', 'e', 3)
lis = ["q", "w", "e"]
func(1, 2, *lis, 3)
(4).函数名加括号被当做参数
实质是先调用函数,再将它的返回值当做别的函数的参数
三、匿名函数:lambda函数
1.语法规则
lambda 参数 : 表达式
lambda只是一个表达式,而不是一个代码块
2.不带参数
f1 = lambda : 123
# 等效于
def f1():
return 123
3.带参数
f2 = lambda a, b: a + b
# 等效于
def f2(a, b):
return a + b
4. 拓展
(1).嵌套匿名函数
f = lambda a, b, func : func(a, b)
f1 = lambda a, b : a + b
f2 = lambda a, b : a - b
y1 = f(1, 2, f1)
y2 = f(1, 2, f2)
print(y1) # 3
print(y2) # -1
(2).实现输入匿名函数执行
f = lambda a, b, fun : fun(a, b)
func_new = input("请输入一个匿名函数:")
func_new = eval(func_new)
y = f(1, 2, func_new)
print(y)
********************************************************************
请输入一个匿名函数:lambda a, b : a + b
3
(3).实现输入匿名函数循环执行
f = lambda a, b, fun : fun(a, b)
while True:
flag = input("请选择是否继续,q,退出;其他,继续:")
if flag == "q":
print("结束运行")
break
else:
func_new = input("请输入一个匿名函数:")
func_new = eval(func_new)
y = f(1, 2, func_new)
print(y)
********************************************************************
请选择是否继续,q,退出;其他,继续:
请输入一个匿名函数:lambda a, b : a + b
3
请选择是否继续,q,退出;其他,继续:q
结束运行
四、作用域
作用域指的是变量的有效范围,Python查找变量顺序:局部–>局部外的局部(闭包)–>全局–>内建
1.局部变量
函数内部定义的变量,为局部变量,函数外无法调用,只能在其被声明的函数内部访问,作用域是局部作用域
在函数内部要修改一个变量,那么这个变量必须是内部变量,除非提前声明为外部变量
2.全局变量
函数外定义的变量,为全局变量,作用域是全局作用域
3.在函数内部定义全局变量
全局作用域,不能在函数内修改,想要在函数内进行修改时
global 变量名
4.函数嵌套情况
函数嵌套时,内层函数想要使用或修改外部的函数变量
nonlocal 变量名
5.注意
重点:Python函数的作用域取决于其函数代码块在整体代码中的位置,而不是调用时机的位置
name = 'jack'
def f1():
name='tom'
def f2():
name ='mary'
print(name)
f2()
f1() # mary
name ='jack'
def f1():
print(name)
def f2():
name = 'eric'
f1()
f2() # jack
五、闭包
1.嵌套
嵌套:我们再f1函数内又定义了一个函数,我们在调用f1函数,f1函数内部又会调用f2函数,这种形式就叫做嵌套
# 定义f1——调用f1——print(f1)——定义f2——调用f2——print(f2)
def f1():
print("f1外层函数")
def f2():
print("f2内层函数")
return f2()
f1()
********************************************************************
f1外层函数
f2内层函数
2.函数名即变量名
def f1():
print("f1外层函数")
a = f1
a()
********************************************************************
f1外层函数
3.闭包
函数里面嵌套函数,外层函数返回内层函数的函数名,这种情况叫做闭包
# 不带参数
def f1():
print("f1外层函数")
def f2():
print("f2内层函数")
return f2
#f1() #f1外层函数
y = f1() # y=f2
print(y) # f2的地址
y() # 调用f2()函数
********************************************************************
f1外层函数
<function f1.<locals>.f2 at 0x0000020E25977510>
f2内层函数
1.直接调用了f1函数,此时,执行print,定义了一个f2函数,同时返回了f2的函数名
2.用变量名y接收f2的函数名
3.变量名+(),实现了函数的调用
# 带参数
def f1(num1):
print("f1外层函数", num1)
def f2(num2):
print("f2内层函数", num2)
return f2
a = f1(666) # a = f2
a(888)
********************************************************************
f1外层函数, 666
f2内层函数, 888
4.总结
1.函数内的变量外部访问不了
2.函数执行完毕后,函数内的变量会被销毁
3.需求:执行完函数后,我们需要保留函数中的某个变量
4.处理:在函数内部去定义一个函数,用内部函数去保存这个变量,我们将内层函数的函数名,当做返回值返回,不调用
5.此时内层函数就把这个需要保留的变量包含在了它的内存空间,这就是闭包
六、递归(慎用)
1.简介
- 递归函数是指一个函数在内部调用了自身,使用时类似于条件循环一样,但是必须要有递归的终止条件
- 递归会占用比较多的内存,当递归次数比较多时,性能就会降低,因此不建议多使用递归
- 递归优点:代码简洁,逻辑清晰
- 递归的核心思想:每一次递归,整体问题都要比原来减小,并且递归到一定层次时,要能直接给出结果
2.递归程序步骤
1.初始化算法
2.检查要处理的当前值是否已经与基线条件相匹配
3.使用更小的或更简单的子问题(或多个子问题)来重新定义答案
4.对子问题运行算法
5.将结果合并入答案的表达式
6.返回结果
3.举例
# 阶乘
def factorial(n):
if n == 1:
return 1
return factorial(n-1)*n
a = factorial(3)
print(a) # 6