-
引子
-
函数的基本使用
-
先定义,后调用
-
定义函数的三种形式
-
调用函数的三种形式
-
函数的返回值
-
函数参数的使用
-
形参系列
-
实参系列
-
可变长系列
函数的基本使用
-
什么是函数?
ps:函数就是盛放代码的容器
具备某一功能的工具--->函数
事先准备工具的过程--->函数的定义
遇到应用场景拿来就用--->函数的调用
函数的使用原则:
先定义
后调用
-
为何要用函数?
解决下述问题:
1.代码组织结构不清晰、可读性差
2.可维护性、可扩展性差
-
如何用函数?
先定义
三种定义方式
后调用
三种调用方式
返回值
三种返回值的形式
定义的语法:
def 函数名(参数1,参数2,参数3,...):
'''函数的文档注释'''
函数体(代码1)
代码2
代码3
return 返回值
# def: 定义函数的关键字;
# 函数名:函数名指向函数内存地址,是对函数体代码的引用。函数的命名应该反映出函数的功能;
# 括号:括号内定义参数,参数是可有可无的,且无需指定参数的类型;
# 冒号:括号后要加冒号,然后在下一行开始缩进编写函数体的代码;
# """文档注释""": 描述函数功能,参数介绍等信息的文档,非必要,但是建议加上,从而增强函数的可读性;
# 函数体:由语句和表达式组成;
# return 值:定义函数的返回值,return是可有可无的。
-
一、定义函数、
定义函数发生的事情
1、申请内存空间把函数体代码放进去
2、将函数内存地址绑定给函数名
强调:定义函数只检测语法,不执行代码
def login(): # login = 函数的内存地址
print(111) # 函数体代码
print(222)
print(333)
print(444)
x = 10 # x = 10的内存地址
print(x) # 原则上打印应该返回内存地址,但是返回的是值。变量名受到python解释器特殊照顾由内存地址直接找到返回值打印。
print(login) # 但函数名不同,打印显示的是内存地址。定义函数的目的是为了把代码丢到容器,只需要运行不需查看。则无需被照顾。
-
二、调用函数
调用函数发生的事情:
1、先通过函数名定为到函数的内存地址
2、函数内存地址()--->触发函数体代码的运行
强调:调用函数才会执行函数体代码
def login():
print(111)
print(222)
login()
# 例1
def func():
print(11111 # 语法错误 定义阶段都无法通过
# 例2:
def func():
print(1111)
x # 逻辑错误,x 没有定义,变量名也需要先定义后引用
print(2222)
func()
# 例3:
# ================
def bar():
print('from bar')
def foo():
print('from foo')
bar()
foo()
================
# 例4:
def foo():
print('from foo')
bar()
foo() # 语法错误,bar没有定义,调用肯定出错
def bar():
print('from bar')
-
三、定义函数的三种形式
1.无参函数
# 例1:函数体不需要外部传参数进行,它本身代码就是固定的,所以就是无参函数
def say():
print('========')
print('welcome to here')
print('========')
say()
# 参数是函数的调用者向函数体传值的媒介,有参无参是根据函数体代码去走的,看函数体代码是否需要外部传参数
def login():
inp_name = input("username>>>: ").strip()
inp_pwd = input("password>>>: ").strip()
if inp_name == "egon" and inp_pwd == "123":
print('login successful')
else:
print("username or password error")
login() # 直接调用无需传参数,input功能已经实现与用户交互
2.有参函数
# 函数体代码需要外部传参数进行,这种函数就需要定义为有参函数
def max2(x,y): # 参数---》原材料
if x > y:
print(x)
else:
print(y)
max2(10,20)
3.空函数
# 函数体为pass代表什么都不做,称之为空函数。定义空函数通常是有用的,因为在程序设计的开始,往往是先想好程序都需要完成什么功能,然后把所有功能都列举出来用pass充当函数体“占位符”,这将使得程序的体系结构立见,清晰且可读性强。例如要编写一个程序,我们可能想到的功能有用户认证,下载,上传,等功能,可以先做出如下定义:
def auth_user():
"""user authentication function"""
pass # 代表占位符
def download_file():
"""download file function"""
pass
def upload_file():
"""upload file function"""
pass
-
四、调用函数的三种形式
# 1.语句
len("hello") # 单纯的调用,例如len统计hello的长度
# 2.表达式
res = len("hello") # 将调用函数的返回值赋值给res
res = len("hello") * 10 # 将调用函数的返回值乘以10的结果赋值给res
print(res)
# 3 .可以当做参数传给另外一个函数
len("hello") # 从形式上看就是将函数调用,从本质讲是跟2同理
print(len("hello")) # 先调len的返回值然后再赋值给print
-
五、函数的返回值
return详解:
def max2(x,y):
if x > y:
return x
else:
return y
res = max2(10,20)
print(res)
# 1、return是一个函数结束的标志,函数可以有多个return,但只要执行一次return,整个函数就立即结束,并且将return后的值当做本次调用的结果返回:
def func():
print("=====") # 只运行到了此行
return 111 # return后的值当做本次调用的结果返回 # 111
print("****")
return 222
print("----------")
return 333
res = func()
print(res)
# 2、return后的返回值有三种情况
# (1)return 值:返回的就是这一个值
# (2)return 值1,值2,值3:返回的一个元组
def func():
return 111,"xxx",[22,33]
res = func()
print(res) # (111,"xxx",[22,33])返回的是元组
# (3)函数内可以没有return、或者return None、或者return:返回的都是None
def func():
print(111)
return
print(2222)
res = func()
print(res)
函数参数的使用
函数参数总体分为两大类:
1、形参:在函数定义阶段括号内指定的参数,称之为形式参数,简称形参----》变量名
2、实参:在函数调用阶段括号内传入的值,称之为实际参数,简称实参----》变量的值
形参与实参的关系是:
在调用函数时,实参值会绑定给形参名,在函数调用完毕后解除绑定
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("egon") # name正常赋值
func("egon",19) # 正常赋值
注意: 可以混用位置形参与默认形参,但是
1、位置形参必须在前面
def func(age = 18, name):
print(name)
print(age) # 报错
2、默认形参的值通常应该是不可变类型
# 例一:
def func(name,hobby,hobbies = []): # 三次调用都用的是同一列表
hobbies.append(hobby)
print('%s的爱好是%s'%(name,hobbies))
func('egon','read') # egon的爱好是['read']
func('tom','play') # tom的爱好是['read', 'play']
func('jack','movie') # jack的爱好是['read', 'play', 'music']
# 例二:
def func(name,hobby,hobbies = None): # 设定hobbies为None
if hobbies is None: # 如果没有为hobbies传值,hobbies默认就是None
hobbies = [] # 每次传值给hobbies,就会产生新的列表
hobbies.append(hobby)
print('%s的爱好是%s' %(name,hobbies))
func('egon','read')
func('tom','play')
func('jack','music')
func('lili','eat',["movie"])
3、默认形参的值是在函数定义阶段赋值的
m = 111
def func(x,y,z = m): # 在定义阶段就把111赋值给了z
print(x)
print(y)
print(z)
m = 666
func(1,2)
-
实参系列:
-
一、位置实参:在调用函数时,按照从左到右的顺序依次传入的值,称之为位置实参
特点:按照位置为形参赋值,一一对应
def func(name,age): # 定义位置形参:name,age,两者都必须被传值
print(name)
print(age)
func("egon") # 报错,少了一个值
func("egon",19) # 正常传值
-
二、关键字实参:在调用函数时,按照key = value的形式传值,称之为关键字实参
特点:可以打乱顺序,但是仍然能够指定道姓的为指定的形参赋值
def func(name,age): # 定义位置形参:name,age,两者都必须被传值
print(name)
print(age)
func(age = 18,name = "egon") # 正常传值
# 注意:可以混用位置实参与关键字实参,但是
# 1、位置实参必须在关键字实参的前面
func("egon",age = 18) # 正常传值
func(age = 18,"egon") # 报错,语法错误
# 2、不能为同一形参重复赋值
def foo(x,y,z):
pass
foo(1,y=2,3) # 报错,语法错误
foo(1,y=2,z=3,x=4) # 报错,语法错误
-
可变长系列
-
可变长系列:
可变长参数指的是在调用函数时,传入的实参个数不固定,对应着必须有特殊形式的形参来接收溢出的实参
实参无非两种形式
溢出的位置实参----->*
溢出的位置关键字实参----->**
*与**在形参中是一种汇总行为
1、*在形参中的应用:*会将溢出的位置实参合并成一个元组,然后赋值给紧跟其后的那个形参名
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变量名(译:参数) 通常搭配*使用 *args接收位置实参的溢出
print(x)
print(args)
func(1,2,3,4,5) # 同上是一个道理
def my_sum(*args): # 求和运算的函数
res = 0
for i in args:
res += i
print(res)
my_sum(1,2)
# 3
2、**在形参中的应用:**会将溢出的关键字实参合并成一个字典,然后赋值给紧跟其后的那个形参名
def func(x,**kwargs): # kwargs = {"a":2,"c":4,"b":3}
print(x)
print(kwargs)
func(1,a=2,b=3,c=4)
# 1
# {'a': 2, 'b': 3, 'c': 4}
=*与**在实参中是一种打散行为===
*在实参中的应用:*后可以跟可以被for循环遍历的任意类型,*会将紧跟其后的那个值打散成位置实参
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
**在实参中的应用:**只能跟字典类型,**会将字典打散成关键字实参
func(**{"k1":111,"k2":2222}) # func(k2=2222,k1=111) 报错,没有k关键字,打散后对应不上
func(**{"x":111,"y":2222,"z":333}) # **将字典打散成关键字实参
# 111
# 2222
# 333
# 例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) # 实参中带*和**是一种打散行为
# 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)
wrapper(1,y=2,z=3)
# 1 2 3
了解(**):命名关键字形参: 在*与**中间的形参称之为命名关键字形参
特点: 必须按照key=value的形式传值
def func(x,m=333,*args,y=222,z,**kwargs):
print(x)
print(args)
print(y)
print(z)
print(kwargs)
func(1,2,3,z=4,a=1,b=2,c=3)
# 1
# (3,)
# 222
# 4
# {'a': 1, 'b': 2, 'c': 3}
func(1,2,3,4,5,6,7) # 报错,命名关键字都是跟在*的后面