week3 day1 函数介绍和函数的参数
一. 函数介绍
1.1 什么是函数?
函数就是盛放代码的容器。
具备某一功能的工具。----->函数
事先准备工具的过程。----->函数的定义
遇到应用场景拿来就用。----->函数的调用
函数的使用规则:(和变量一样)
- 先定义
- 后调用
1.2 为何要使用函数?
因为我们之前学过的内容在需要在程序内多处想要使用的时候,只能复制粘贴,这会导致两个问题。一,代码繁杂不美观,可读性差,二,如果想修改这个功能,必须在所有地方都修改功能,代码的可扩展性较差。
1.3 如何使用函数?
函数的使用遵循先定义后调用。在定义阶段,遵循以下语法规范:
def 函数名(参数1,参数2.。。。):
函数体代码
return 返回值
# def: 定义函数的关键字;
# 函数名:函数名指向函数内存地址,是对函数体代码的引用。函数的命名应该反映出函数的功能;
# 括号:括号内定义参数,参数是可有可无的,且无需指定参数的类型;
# 冒号:括号后要加冒号,然后在下一行开始缩进编写函数体的代码;
# """文档描述""": 描述函数功能,参数介绍等信息的文档,非必要,但是建议加上,从而增强函数的可读性;
# 函数体:由语句和表达式组成;
# return 值:定义函数的返回值,return是可有可无的。
函数定义阶段发生的事情(和定义变量差不多):
- 申请一块内存空间,将函数代码块保存在里面
- 将内存空间对应的内存地址赋值给函数名
- 函数定义阶段只检查语法规范,不运行函数体代码块
函数调用阶段发生的事情:
- 直接访问函数名,即访问函数名绑定的内存地址
- 通过加()的方式运行函数体代码快内的代码
- 函数调用阶段运行函数体代码块
def func():
pass
print(func)----->内存地址
让我们通过下面几个例子来更扎实掌握关于函数定义和调用阶段的知识点:
例一:函数定义阶段只检测语法,不运行代码
def func():
print('from func'
# 函数体少右半括号,属于语法错误,直接报错
例二:调用阶段才运行代码
def func():
print('from func before x')
x
print('from func after x')
# x没有定义,属于逻辑错误不属于语法错误,调用函数时才会报错
例三:只要在调用函数前函数定义完毕就不会报错
def foo():
print('from foo')
bar()
def bar():
print('from bar')
foo() # ----->'from foo'
# 'from bar'
1.4 定义函数的三种方式
-
无参函数
def without_argument(): print('from without argument')
应用场景:不需要外界传参时使用。
-
有参函数
def with_argument(x,y): print(x,y)
应用场景:需要外界传参时使用。
-
空函数
def empty(): ...
应用场景:搭函数架构时使用,占位。
1.5 函数的三种调用方法
- 语句
len('hello')
- 表达式
res=len('hello') print(res)
- 当作参数传给另一个函数
print(len('hello'))
1.6 函数的返回值
返回值相当于函数加工工厂的产品。看这个工厂需不需要产品输出。return
是函数结束的标志,即函数体代码一旦运行到return
会立刻终止函数的运行,并且会将return
后的值当作本次运行的结果返回。
- 返回
None
:函数体内没有return
或return
没有返回值 - 返回一个值:
return 值
- 返回多个值:用逗号分割开多个值,会被return返回成元组
return 10,'string'
def func():
print(111)
return
print(222)
print(333)
func()# --->111 函数运行到return会立刻结束函数体代码的运行
二. 函数参数
2.1 参数的分类——形参和实参
形参:定义函数时括号内指定的参数。(变量名)
实参:调用函数时括号内传入的参数。(变量值)
形参和实参的关系:
在调用函数时,实参值会绑定给形参名。在函数调用完毕后解除。
形参:
形参分类 | 定义 | 特点 | 使用 |
---|---|---|---|
位置形参 | 在定义函数时,按照从左到右的顺序依次定义的变量名,称之为位置形参 | 每次调用,必须为位置形参赋值 | def func(x,y): ... |
默认形参 | 在定义函数时,就已经为某个形参赋值了 | 调用函数时,可以不为其赋值 | def info(name,age=18): ... |
强调:位置形参可以和默认形参混用,但
- 位置形参必须在默认形参前
- 默认形参的值一般是不可变类型
扩展:如果默认形参是可变类型数据的情况以及这种情况的改进版本:
def func(name,hobby,hobbies=[]):
hobbies.append(hobby)
print(name,hobbies)
func('wth','drive') # ----->wth ['drive']
func('wth1','read') # ----->wth1 ['drive','read']
func('wth2','music') # ----->wth2 ['drive','read','music']
升级版:
def func(name,hobby,hobbies=None):
if hobbies is None:
hobbies=[]
hobbies.append(hobby)
print(name,hobby)
func('wth','drive') # ----->wth ['drive']
func('wth1','eat',['play']) # ----->wth1 ['play','eat']
m到底等于几???
m=111
def func(x,y,z=m):
print(m)
m=666
func(1,2)
实参:
实参分类 | 定义 | 特点 | 使用 |
---|---|---|---|
位置实参 | 在调用函数时,按照位置给函数传参 | 按照位置为形参传值,一一对应 | func('wth',18) |
关键字实参 | 在调用函数时,按照key=value传值 | 可以打乱顺序,但仍然能够指名道姓为形参赋值 | func(age=18,name='wth') |
强调:位置实参和关键字实参可以混用,但
- 位置实参必须在关键字实参之前
- 不可以为一个形参多次传值
2.2 可变长系列
可变长系列指的是在调用函数时,传入的参数个数不固定,对应着必须有特殊形式来接收溢出的实参。实参无非有两种形式:
-
用
*
来接收溢出的位置实参 -
用
**
来接收溢出的关键字实参
2.2.1 在形参的应用
*
在形参中的应用:会将溢出的位置实参合并成一个元组,赋值给紧跟在后的args,相当于for
循环def func(x,*args): print(x,args) func(1,2,3,4)----->1 (2,3,4)
案例:加法计算器 def my_sum(*args): res=0 for i in args: res+=i print(res) my_sum(23,34,45,56)----->158
**
在形参中的应用:会将溢出的关键字实参合并成一个字典,赋值给紧跟其后的kwargsdef func(x,y,**kwargs): print(x,y,kwargs) func(y=3,x=2,z=5)----->2 3 {'z':5}
2.2.2 在实参的应用
*
在实参中的应用:会将可迭代元素打散成位置实参(str,list,tuple,dict),相当于for
循环def func(x,y,z): print(x,y,z) func(*(1,2,3))----->1 2 3 func(*[1,2,3])----->1 2 3 func(*'hel')----->'h' 'e' 'l' func(*{'a':1,'b':2,'c':3})----->'a' 'b' 'c'
**
在实参中的应用:会将字典类型打散成关键字实参,**
只能跟字典def func(x,y,z): print(x,y,z) func(**{'x':34,'y':67,'z':89})----->34 67 89
为装饰器内容的铺垫:(*******)
def index(x,y,z):
print(x,y,z)
def wrapper(*args,**kwargs):
index(*args,**kwargs)
调用wrapper传入的参数被*和**汇总,又被index函数的*和**打散,相当于对wrapper传入的参数原封不动的传入index。即对于wrapper传入参数要关注index是如何定义的。