函数
- 是一段代码的表示
- 一段具有特定功能的、可重用的语句组
- 两个作用:降低编程难度和代码的复用
1. def关键字
- 定义一个函数
- def关键字,后跟一个空格
-函数名,自己定义,起名需要遵循便令命名规则,约定俗成,不要使用大驼峰(只给类起名用) - 后面括号和冒号不能省,括号内可以有参数,也可以没有
- 只是定义的话不会执行
- 注意缩进,形同C语言的{ },如果表示在函数内就不能与def齐平;函数内有赋值计算分支循环等结构,也必须非常注意缩进,缩进表示"语句地位,语句从属"
例:
def func():
print("我是一个函数")
print("我要完成一定功能")
print("我结束了")
func()
我结束了 #第三句print,与def齐平,则不在函数内,直接就输出了
#此时函数调用
我是一个函数
我要完成一定功能
2.函数的参数和返回值
- 参数: 负责给函数传递一些必要的数据或者信息
- 形参(形式参数): 在函数定义的时候用到的参数没有具体值,只是一个 占位的符号,成为形参
- 实参(实际参数): 在调用函数的时候输入的值
- 返回值: 函数的执行结果
- return保留字用来传递返回值
- 函数可以有返回值,也可以没有,可以有return,也可以没有
- return可以传递0个返回值,也可以传递任意多个返回值
- 如果没有return,默认返回一个None,即空
- 函数一旦执行return语句,则无条件返回,即结束函数的执行
例1:
def hello(person):
print("{0}, 你肿么咧".format(person))
print("我上班去了,你照顾好自己,有事call我!")
return "我已经说完了,{}bye".format(person)
p = "Honey"
hello(p)
Honey, 你肿么咧
我上班去了,你照顾好自己,有事call我!
我已经说完了,person,bye
#调用时给出实际参数(即"honey"),实际参数代替定义中的形式参数(即 "person"),然后运行函数,逐条执行,最后return执行(return语句的基本使用
函数打完招呼后返回一句话),即结束函数
例2:
def hello(person):
print("{0}, 你肿么咧".format(person))
return "肿么这么严重,我今天请了一天假,照顾你"
print("我上班去了,你照顾好自己,有事call我!")
return "我已经说完了,{}bye".format(person)
p = "Honey"
hello(p)
Honey, 你肿么咧
'肿么这么严重,我今天请了一天假,照顾你'
#return直接结束函数,不运行下面语句
3.函数参数细分类
1、 普通参数
- 定义的时候直接定义变量名
- 调用的时候直接把变量或者值放入指定位置
def 函数名(参数1, 参数2,…):
函数体
函数名(value1, value2,…) #调用函数
# 调用的时候,具体值参考的是位置,按位置赋值
2、默认参数
- 形参带有默认值
- 调用的时候,如果没有对相应形参赋值,则使用默认值
例1:
def func_name(p1=v1, p2=v2..........):
func_block
func_name() #调用形式1,使用默认值
value1= 100
value2=200
func_name(value1,value2)
#调用形式2,对相应形参赋值
例2:
def reg(name, age, gender="male"):
if gender == "male":
print("{0} is {1} and he is a good student".format(name, age))
else:
print("{0} is {1} and she is a good student".format(name, age))
reg("xiaoming", 21)
reg("xiaohong", 20, "female")
xiaoming is 21 and he is a good student
xiaohong is 20 and she is a good student
#如果不给第三个值即默认为"male"(之前形参带有默认值gender="male"),当第三个值为female时,不同则执行else下的语句
3、关键字参数
- 调用函数时,直接给形参赋值再调用函数
def func(p1=v1, p2=v2…):
func_body
func(p1=value1, p2=value2…)- 比较麻烦,但也有好处:不容易混淆, 一般实参和形参只是按照位置一一对应即可,容易出错使用关键字参数,可以不考虑参数位置
例:
- 比较麻烦,但也有好处:不容易混淆, 一般实参和形参只是按照位置一一对应即可,容易出错使用关键字参数,可以不考虑参数位置
def stu_key(name="No name", age=0, addr="No addr"):
print("I am a student")
print("我叫 {0}, 我今年 {1}岁了, 我住{2}".format(name, age, addr))
stu_key(age=18, name="小明", addr="中国")
I am a student
我叫 小明, 我今年 18岁了, 我住中国
4、收集参数(可变参数)
- 把没有位置,不能和定义时的参数位置相对应的参数,放入一个特定的数据结构中
def func(*args):
func_body
#按照list使用方式访问args得到传入的参数
func(p1, p2, p3, …)- 参数名args不是必须这么写,但是,我们推荐直接用*args,约定俗成
- 参数名args前需要有星号
- 收集参数可以和其他参数共存
例:
def stu( *args):
print("Hello 大家好,我自我介绍以下,简答说两句:")
# type函数作用是检测变量的类型
print(type(args))
for item in args:
print(item)
stu("小刚", 18, "住北京中关村", "冒出一个名叫小怪的人", "打酱油")
stu("小玉")
Hello 大家好,我自我介绍以下,简答说两句:
<class 'tuple'> #检测出为元组类型
小刚
18
住北京中关村
冒出一个名叫小怪的人
打酱油
Hello 大家好,我自我介绍以下,简答说两句:
<class 'tuple'>
小玉
5、收集参数之关键字收集参数
- 把关键字参数按字典格式存入收集参数
def func( **kwargs):
func_body
func(p1=v1, p2=v2, p3=v3…)
- ** kwargs一般约定俗成
- 调用的时候,把多余的关键字参数放入kwargs
- 访问kwargs需要按字典格式访问
例:
def stu( **kwargs):
print("Hello 大家好,我先自我介绍一下:")
print(type(kwargs))
# 在函数体内对于kwargs的使用不用带星号
for k,v in kwargs.items():
print(k,v)
stu(name="小明",age=18 ,addr="北京中关村", lover="you", work="Teacher")
print("*" * 50)
stu(name="小刚")
Hello 大家好,我先自我介绍一下:
<class 'dict'>
name 小明
age 18
addr 北京中关村
lover you
work Teacher
**************************************************
Hello 大家好,我先自我介绍一下:
<class 'dict'>
name 小刚
#调用的时候需要使用关键字参数调用,把关键字参数按字典格式存入收集参数,在遍历其中两个元素时,也成对字符的形式被遍历出来,当遍历其中“一个元素”时,以元组(还是成对的)的形式被遍历出来
6、收集参数混合调用的顺序问题
- 收集参数,关键字参数,普通参数可以混合使用
- 使用规则就是,普通参数和关键字参数优先
- 定义的时候一般找普通参数,关键字参数,收集参数tuple,收集参数dict
例:
def stu(name, age, *args, hobby="没有", **kwargs):
print("Hello 大家好")
print("我叫 {0}, 我今年{1}了。".format(name, age))
if hobby == "没有":
print("我没有爱好, so sorry")
else:
print("我的爱好是{0}".format(hobby))
print("*" * 20)
for i in args:
print(i)
print("#" * 30)
for k,v in kwargs.items():
print(k, "---", v)
name = "xiaoyu"
age = 19
stu(name, age, "爱好多", "扔石头", hobby="游泳", hobby2="烹饪",\
hobby3="跟不同女生聊天")
#当代码句子过长时,可以加 \ 然后换下行继续写就行了
Hello 大家好
我叫 xiaoyu, 我今年19了。
我的爱好是游泳
********************
爱好多
扔石头
##############################
hobby2 --- 烹饪
hobby3 --- 跟不同女生聊天
7、收集参数的解包问题
- 把参数放入list或者字典中,直接把list/dict中的值放入收集参数中
- 语法:参看案例
例:
def stu(*args):
print("**********")
n = 0
for i in args:
print("第{}次解包".format(n))
n += 1
print(i)
d = type(i)
print("类型是{}".format(d))
print(" ")
l = ["xiaohong", 19, 23, "xiaodong"]
# 此时,args的表示形式是字典内一个list类型的元素,即 arg = (["xiaohong", 19, 23, "xiaodong"],) ,很显然跟我们最初的想法违背
# 此时的调用,我们就需要解包符号,即调用的时候前面加一个星号
stu(*l)
**********
第0次解包
xiaohong
类型是<class 'str'>
第1次解包
19
类型是<class 'int'>
第2次解包
23
类型是<class 'int'>
第3次解包
xiaodong
类型是<class 'str'>
## 同理,dict类型收集参数一样可以解包,但是
- 对dict类型进行解包
- 需要用两个星号进行解包
8、函数文档
- 函数的文档的作用是对当前函数提供使用相关的参考信息,方便其他人查看检验,相当于给他人提供函数说明书
-文档的写法:- 在函数内部开始的第一行使用三引号字符串定义符
- 一般具有特定格式
-文档查看: - 使用help函数,形如 help(func)
- 使用__doc__,(doc仅仅是函数的一个属性,所以比help显示简洁得多)
例:
def stu(name, age ,*args):
# '这是一个文档现在就介绍一下函数作用等'
print("This is hanshu stu")
help(stu)
stu.__doc__
Help on function stu in module __main__:
stu(name, age, *args)
这是一个文档现在就介绍一下函数作用等
'这是一个文档现在就介绍一下函数作用等'
#一行介绍可以使用一对单引号或双引号
规范标准案例:
def stu(name, age):
'''
这是文档的文字内容
:param name: 表示学生的姓名
:param age: 表示学生的年龄
:return: 此函数没有返回值
'''
pass
print(help(stu))
print("*" * 20)
print(stu.__doc__)
Help on function stu in module __main__:
stu(name, age)
这是文档的文字内容
:param name: 表示学生的姓名
:param age: 表示学生的年龄
:return: 此函数没有返回值
None
********************
这是文档的文字内容
:param name: 表示学生的姓名
:param age: 表示学生的年龄
:return: 此函数没有返回值
4.变量作用域
-变量由作用范围限制
-分类:按照作用域分类
- 全局(global): 在函数外部定义
- 局部(local):在函数内部定义
-变量的作用范围:- 全局变量:在整个全局范围都有效
- 全局变量在局部可以使用
- 局部变量在局部范围可以使用
- 局部变量在全局范围无法使用
- LEGB原则
- L(Local)局部作用域
- E(Enclosing function locale)外部嵌套函数作用域(外围空间)
- G(Global module)函数定义所在模块(全局)作用域
- B(Buildin): python内置的作用域
详解: https://blog.csdn.net/geekcome/article/details/9072285###
- 局部变量和全局变量是不同变量
- 局部变量是函数内部的占位符,与全局变量可能重名但不同
- 函数韵运算结束后,局部变量被释放("被释放"即在函数完成,局部变量将被删除,局部变量只用于函数内部)
例:
n, s =10, 100 #n和s是全局变量
def fact(n) :
s = 1 #fact()函数中的n和s是局部变量
for i in range(1, n+1):
s *= i
return s
print(fact(n), s) #n和s是全局变量
3628800 100
-
可以使用global保留字在函数内部使用全局变量
例:n, s =10, 100 def fact(n) : global s #fact()函数中使用global保留走声明此处s是全局变量s for i in range(1, n+1): s *= i return s #此处s指全局变量s print(fact(n), s) #此处全局变量s被函数修改 362880000 362880000
5.eval()函数
-
把一个字符串当成一个表达式来执行, 返回表达式执行后的结果
-
语法:
eval(string_code, globals=None, locals=None)
6.exec()函数
- 跟eval功能类似, 但是,不返回结果
- 语法:
exec(string_code, globals=None, locals=None)
例:
x = 100
y = 200
z1 = x + y
z2 = eval("x+y")
print(z1)
print(z2)
300
300
例:
x = 100
y = 200
z1 = x + y
z2 = exec("x+y")
print(z1)
print(z2)
300
None
6.递归
-定义
- 类似数学归纳法
- 证明当n取第一个值n0时命题成立
- 假设当nk时命题成立,证明当n=nk时命题成立
- 递归是数学归纳法思维的编程体现
-递归的实现 - 递归本身是一个函数,需要函数定义方法描述
- 函数内部,采用分支语句对输入参数进行判断
- 基例和链条,分别编写对应代码
- 基本通过调用自身来实现
- 计算n!
- 当n=0时,结果为1,
-其他情况时n! = n*(n-1)!
例:
def fact(n):
if n == 0:
return 1
else :
return n*fact(n-1)
n = eval(input("请输入一个数<int类型>:"))
fact(n)
-斐波那契数列
例:
def fun(n):
if n == 1 or n == 2:
return 1
else :
return fun(n-1) + fun(n-2)
n = eval(input("请输入一个数<int类型>:"))
fun(n)
-汉诺塔问题
- 规则:
- 每次移动一个盘子
- 任何时候大盘子在下面,小盘子在上面
- 方法:
- n=1: 直接把A上的一个盘子移动到C上, A->C
- n=2:
- 把小盘子从A放到B上, A->B
- 把大盘子从A放到C上, A->C
- 把小盘子从B放到C上, B->C
- n=3:
- 把A上的两个盘子,通过C移动到B上去, 调用递归实现
- 把A上剩下的一个最大盘子移动到C上, A->C
- 把B上两个盘子,借助于A,挪到C上去, 调用递归
- n = n:
- 把A上的n-1个盘子,借助于C,移动到B上去,调用递归
- 把A上的最大盘子,也是唯一一个,移动到C上,A->C
- 把B上n-1个盘子,借助于A,移动到C上, 调用递归
- 方法:
例:
def hano(n, src, mid, dst):
'''
汉诺塔的递归实现
n:代表几个盘子
src:代表第一个塔,开始的塔
mid:代表第二个塔,中间过渡的塔
dst:代表第三个塔, 目标塔
'''
if n == 1:
print(src, "-->", dst)
return None
hano(n-1, src, dst, mid) # 把n-1个盘子,从A塔借助于C塔,挪到B塔
print(a, "-->", c)
hano(n-1,mid, src, dst) # 把n-1个盘子,从B塔,借助于A塔,挪到C塔
n = eval(input("请输入圆盘个数:")) #调用函数
src = "A"
mid = "B"
dst = "C"
hano(n, scr, mid, dst)
7.lambda函数
-一种匿名函数,即没有名字的函数
-使用lambda保留字定义,函数名是返回结果
-
lambda函数用于定义简单的,能够在一行内表示的函数
-
形式:
<函数名> = lambda<参数>:<表达式>
等价于
def <函数名>(<参数>): <函数体> return <返回值>
例1:
f = lambda x, y :x + y
f(10,15)
25
例2:
f = lambda : "lambda函数"
print(f()
lambda函数