函数的基本使用
什么是函数
函数就类似于具备某一功能的工具,提前定义好之后可以反复使用(即:函数就是盛放代码和功能的容器)

函数的使用原则:
先定义 ,后调用
为何要用函数==>解决下述问题:
如何用函数
函数定义的语法:
def 函数名(参数1,参数2,参数3,...):'''函数的注释'''函数体代码return 返回值'''1.def: (必须)定义函数的关键字;2.函数名:(必须)函数名指向函数内存地址,是对函数体代码的引用。函数的命名应该反映出函数的功能;3.括号冒号:(必须)括号后要加冒号,然后在下一行开始缩进编写函数体的代码;4.参数:(可选)括号内定义参数,参数是可有可无的,且无需指定参数的类型;5.函数注释: (可选)描述函数功能,参数介绍,非必要,但是建议加上,从而增强函数的可读性;6.函数体:(必须)由语句和表达式组成;7.return值:(可选)定义函数的返回值,return是可有可无的。'''
函数的定义与调用
定义函数
定义函数发生的事情
- 1、在内存空间中申请一块内存空间存储函数体代码
- 2、将函数体代码所在的内存空间地址绑定给函数名
强调:定义函数只检测语法,不执行代码
def login(): # login = 函数的内存地址print(111) # 函数体代码print(222)x = 10 # x = 10的内存地址print(x) # 10'''原则上打印应该返回内存地址,但是返回的是值。变量名受到python解释器特殊照顾由内存地址直接找到返回值打印。'''print(login) # <function login at 0x7fa136db91f0>'''但函数名不同,打印显示的是内存地址。定义函数的目的是为了把代码丢到容器,只需要运行不需查看。则无需被照顾。'''
调用函数
调用函数发生的事情:
- 1、先通过函数名定位到函数的内存地址
- 2、函数名加括号会触发函数体代码的运行
强调:调用函数才会执行函数体代码
# 示例:def login():print('hello')login() # hello 触发函数体代码的运行# 语法错误def func():print(111 # 无效语法,定义阶段都无法通过# 逻辑错误def func():print(111)x # x 没有定义,变量名也需要先定义后引用func() # 调用肯定出错# 示例:def bar():print('from bar')def foo():print('from foo')bar()foo()'''from foofrom bar'''# 语法错误def foo():print('from foo')bar()foo() # bar没有定义,调用肯定出错def bar():print('from bar')
定义函数的三种形式
1.无参函数
- 函数在定义阶段括号内没有参数
# 函数体不需要外部传参数进行,它本身代码就是固定的,所以就是无参函数def say():print('welcome to here')say()# 参数是函数的调用者向函数体传值的媒介,有参无参是根据函数体代码去走的,看函数体代码是否需要外部传参数def login():inp_name = input("请输入用户名>>>: ").strip()inp_pwd = input("请输入密码>>>: ").strip()if inp_name == "jack" and inp_pwd == "123":print('登录成功!')else:print("用户名或密码错误")login() # 直接调用无需传参数,input功能已经实现与用户交互
2.有参函数
- 函数在定义阶段括号内有参数
# 函数体代码需要外部传参数进行,这种函数就需要定义为有参函数def max2(x, y): # 参数---》原材料if x > y:print(x)else:print(y)max2(10,20)
3.空函数
- 函数在定义阶段括号内没有参数,并且函数体代码为空(pass)
# 函数体为pass代表什么都不做,称之为空函数。定义空函数通常是有用的,因为在程序设计的开始,往往是先想好程序都需要完成什么功能,然后把所有功能都列举出来用pass充当函数体“占位符”,这将使得程序的体系结构清晰且可读性强。例如要编写一个程序,我们可能想到的功能有用户认证,下载,上传,等功能,可以先做出如下定义:def auth_user():"""用户认证"""pass # 代表占位符def download_file():"""下载文件"""passdef upload_file():"""上传文件"""pass
调用函数的三种形式
# 1.语句len("hello") # 单纯的调用,例如len统计hello的长度# 2.表达式res = len("hello") # 将调用函数的返回值赋值给resres = len("hello") * 10 # 将调用函数的返回值乘以10的结果赋值给resprint(res)# 3 .可以当做参数传给另外一个函数len("hello") # 从形式上看就是将函数调用,从本质讲是跟2同理print(len("hello")) # 先调len的返回值然后再赋值给print
函数的返回值
return 详解
def max(x, y):if x > y:return xelse:return yres = max(10, 20)print(res)
1、return是一个函数结束的标志,函数可以有多个return,但只要执行一次return,整个函数就立即结束,并且将return后的值当做本次调用的结果返回
```python def func(): print(“=====”) # 只运行到了此行 return 111 # return后的值当做本次调用的结果返回 print(“**“) # ruturn 下面的值不会被执行 return 222
res = func() print(res)
‘’’
111 ‘’’
-<a name="0cee9588"></a>#### 2、return后的返回值有三种情况- 1)函数内可以没有return 或者 return None 或者 return:返回的都是None- 2)return 值:返回的就是这一个值- 3)return 值1,值2,值3:会自动组织成一个元组返回```python# 示例1:def func():print(111)returnprint(2222)res = func()print(res)'''111None'''# 示例2:def func():return 111,"xxx",[22,33]res = func()print(res) # (111,"xxx",[22,33])返回的是元组

函数参数的使用
形参与实参的关系:
我们可以将形参看成是变量名,实参看成是变量值,在调用函数时,实参值会绑定给形参名,在函数调用完毕后解除绑定
def func(x, y): # x, y是形参,在定义阶段()内传值。形参指的是变量名。print(x, y)func(111, 222) # 111,222是实参,在调用阶段传送的值,实参指的是变量值"""形参的表现形式只有一种就是变量名实参的表现形式有很多种(但是把握核心就是:数据值)"""
形参系列
位置形参
- 在函数定义阶段按照从左往右的顺序依次定义的变量名
- 特点:
- 每次调用必须被赋值,按照位置一一对应
- 位置参数在绑定的时候多一个不行少一个也不行
def func(x, y): # 定义位置形参:x,y,两者都必须被传值print(x)print(y)func(1, 2) # 可以正常赋值,必须刚刚好func(1, 2, 3) # 报错,多了一个值func(1) # 报错,少了一个值
默认形参
- 在函数定义阶段就已经为某个形参赋值了
- 特点:
- 在函数调用阶段,可以不用为其赋值,则使用默认的
- 在函数调用阶段,也可以继续为其赋值, 则使用你给的
def func(name, age=18): # age=18是默认形参,无需赋值,也可以被赋值print(name)print(age)func("jack") # name正常赋值func("jack", 19) # 正常赋值
# 例一:def func(name, hobby, hobbies = []): # 三次调用都用的是同一列表hobbies.append(hobby)print('%s的爱好是%s'%(name,hobbies))func('jack','read') # jack的爱好是['read']func('tom','play') # tom的爱好是['read', 'play']func('bob','movie') # bob的爱好是['read', 'play', 'music']'''格式越简单的越靠前 格式越复杂的越靠后'''# 例二:m = 111def func(x, y ,z=m): # 在定义阶段就把111赋值给了zprint(x)print(y)print(z)m = 666func(1, 2)
实参系列
位置实参
- 在函数调用阶段括号内按照从左往右的顺序依次传入的数据值
- 特点:
- 按照位置为形参赋值,按照位置一一对应
- 位置参数在绑定的时候多一个不行少一个也不行
def func(name, age): # 定义位置形参:name,age,两者都必须被传值print(name)print(age)func("jack") # 报错,少了一个值func("jack",19) # 正常传值
关键字实参
- 在函数调用阶段,按照 形参名 = 数据值 的形式指名道姓的传值
- 特点:可以打乱顺序,但是仍然能够指定道姓的为指定的形参赋值
def func(name, age): # 定义位置形参:name,age,两者都必须被传值print(name)print(age)func(age = 18,name = "jack") # 正常传值
# 例一:def func(name, age): # 定义位置形参:name,age,两者都必须被传值print(name)print(age)func("jack",age = 18) # 正常传值func(age = 18,"jack") # 报错,语法错误'''格式越简单的越靠前 格式越复杂的越靠后'''# 例二:def foo(x, y, z):passfoo(1, y=2,3) # 报错,语法错误foo(1, y=2, z=3, x=4) # 报错,语法错误

可变长参数
- 可变长参数指的是在函数调用阶段,传入的实参个数不固定,对应着必须有特殊形式的形参来接收多余的实参
- 实参无非两种形式
- 多余的位置实参 *
- 多余的位置关键字实参 **
与*在形参中是一种汇总行为
* 在形参中的使用
# 函数无论传入多少位置参数都可以正常运行def func(x, *y): # *是接收多余位置实参的功劳,不是y变量名的功劳。*后面可以跟任意变量名。print(x)print(y)func(1, 2, 3, 4, 5) # x = 1 , y = (2,3,4,5)'''1(2,3,4,5)'''def func(x, *args): # args变量名通常搭配*使用print(x)print(args)func(1, 2, 3, 4, 5) # 同上是一个道理def my_sum(*args): # 求和运算的函数res = 0for i in args:res += iprint(res)my_sum(1, 2) # 3
** 在形参中的使用
# 函数无论传入多少关键字参数都可以正常运行def func(x, **kwargs): # kwargs变量名通常搭配**使用print(x)print(kwargs)func(1,a=2,b=3,c=4)'''1{'a': 2, 'b': 3, 'c': 4}'''# 定义一个函数无论传入多少位置参数和关键字参数都可以正常运行def index(*a,**b):print(a,b)index() # () {}index(1,2,3,4) # (1, 2, 3, 4) {}index(a=1,b=2,c=3) # () {'a': 1, 'b': 2, 'c': 3}index(1,2,3,4,a=1,b=2,c=3) # (1, 2, 3, 4) {'a': 1, 'b': 2, 'c': 3}"""墨守成规可变长形参 *与**后面的变量名其实是可以随便定义的但是python中推荐使用*args**kwargsdef index(*args, **kwargs):pass"""
与*在实参中是一种打散行为
* 在实参中的使用
def func(x, y, z):print(x)print(y)print(z)func([11, 22, 33]) # 报错func(*[11,22,33]) # func(11,22,33) *会把列表这种实参打散成位置实参func(*"hello") # func("h","e","l","l","o") 报错,但能证明*的打散行为func(*{"k1":111,"k2":2222}) # func("k1","k2") 报错,但证明*能把字典打散成key
** 在实参中的使用
# ** 只能跟字典类型def func(x, y, z):print(x)print(y)print(z)func(**{"k1": 111, "k2": 2222}) # func(k2=2222,k1=111) 报错,没有k关键字,打散后对应不上func(**{"x": 111, "y": 2222, "z": 333}) # **将字典打散成关键字实参'''1112222333'''# 例1:def wrapper(*args, **kwargs): # 形参中带*和**是一种汇总行为print(args)print(kwargs)wrapper(1, 2, 3, 4, 5, 6, a=1, b=2, c=3)'''(1, 2, 3, 4, 5, 6){'a': 1, 'b': 2, 'c': 3}'''# 例2:def index(x, y, z):print(x, y, z)def wrapper(*args, **kwargs):index(*args, **kwargs)wrapper(1, y=2, z=3) # 1 2 3'''实参中带 * 和 ** 是一种打散行为index(*(1, 2, 3, 4, 5, 6), **{"a": 1, "b": 2, "c": 3})index(1, 2, 3, 4, 5, 6, a=1, b=2, c=3)'''
命名关键字形参: 在与*中间的形参称之为命名关键字形参(了解)
特点: 必须按照key=value的形式传值
def register(name,age,*,sex,height):pass# register('jason',18,'male',183)register('lili',18,sex='male',height='1.8m') #正确使用"""sex height在传入实参的时候必须以关键字参数的形式ps:该类型的参数几乎不用 也几乎很少能碰到"""
