17.python函数的基本使用

其实函数我们在之前就已经接触过了,输入输出的input\print,类型转换的int\bool等等很多很多,他们都是具有一定的功能。

简单理解,函数就是一个个的功能模块,他的作用其实很容易理解,我们写的程序会越来越复杂、功能越来越多,但是很多时候需要重复使用一些功能代码,函数其实就是将这些功能代码封装起来做成工具

print函数为例,我们可能会在一个程序中大量使用print,如果每用一次就完整书写一次print内部代码,就会导致代码非常臃肿,组织结构不清晰,可读性差,这个叫做代码冗余,而且如果要对程序的print进行修改,我们还需要修改代码中每一个使用了这个功能的代码,就会导致可维护性、扩展性差


函数基础

函数分为两个大类,我们之前接触到可以直接使用的函数,其实是python解释器提前定义好的,称为内置函数,但是内置函数只提供了基本的功能。而很多功能需要我们自己定义成函数,这种叫做自定义函数

函数的使用必须遵循先定义,后调用的原则。

函数的定义就相当于事先将函数体代码保存起来,然后将内存地址赋值给函数名,函数名就是对这段代码的引用,这类似于变量的定义

def 函数名(参数1,参数2,...):
    """文档描述"""
    函数体
    return
  • def: 定义函数的关键字

  • 函数名:函数名指向函数内存地址,是对函数体代码的引用。函数的命名应该反映出函数的功能

  • 括号:括号内定义参数,参数是可有可无的,且无需指定参数的类型

  • 冒号:括号后要加冒号,然后在下一行开始缩进编写函数体的代码

  • “”“文档描述”"": 描述函数功能,参数介绍等信息的文档,文档描述是可有可无的,但是建议加上,从而增强函数的可读性

    # 查看文档描述
    print(help(函数名))
    
  • 函数体:由语句和表达式组成

  • return 值:定义函数的返回值,*返回值是可有可无的

定义与调用函数发生的事情

定义函数发生的事情

  1. 申请内存空间保存函数体代码
  2. 将上述内存地址绑定给函数名
  3. 定义函数不会执行函数体代码,但是会检测函数体语法

调用函数发生的事情

  1. 通过函数名找到函数的内存地址
  2. 然后加括号就是再触发函数体代码的运行

定义函数

# 函数名的命名规范与变量名类似
# 函数名的命名风格推荐小写加下划线,但函数是工具,最好是动词而不是名词

# 形式一:无参函数
def interactive():
    name = input('username>>>:') # 这里是函数体内接受用户输入
    age = input('age>>>:')
    print('名字:{},年龄:{}'.format(name,age))
interactive() # 直接函数名调用

# 形式二:有参函数
def add(x,y): # 接收参数的值可以被函数体所使用
    c = x + y
    return c # return着返回值
result = add(10,20) # 外部接收返回值
print(result)

# 形式三:空函数,函数体代码为pass的函数被称为空函数
# 空函数常使用在构思代码的时候,将函数体预先写出占位,最好写上注释计划的功能
def func(x,y):
    '''计划做的功能'''
    pass
func(1,2) # 没有任何执行

调用函数

def add(x,y):
    z = x + y
    return z

# 形式一:语句形式 - 只调用函数,不用接收返回值
add(1,2) # 没有任何返回值

# 形式二:表达式形式
print(add(3,4))

res = add(5,6)
print(res)

res = add(3,3) * 12
print(res)

# 形式三:函数调用作为参数
res = add(add(1,2),3) # 1+2+3
print(res)

函数返回值

return是函数体结束的标志,即函数体代码一旦运行到return就会立即终止函数的运行,并且会将return后的值当成结果返回

# 没有返回值:返回None
def add(x,y):
    z = x + y
    return # 函数内没有return也是返回None
print(add(1,2)) # None

# 返回一个值
def add(x,y):
    z = x + y
    return z # 函数内没有return也是返回None
print(add(1,2))

# 返回多个值:return用逗号分隔多个值,返回元组
def add(x,y):
    z = x + y
    return x,y,z # 函数内没有return也是返回None
res = add(1,2)
print(res,type(res)) # (1, 2, 3) <class 'tuple'>

函数的参数

函数的参数分为形式参数和实际参数,简称形参和实参

引用传递:在python中所有值的传递都不是传递的值本身,而是值的内存地址

形参与实参的关系

# x,y为形式参数
def add(x,y):
    pass
add(1,2) # 1,2为实际参数

形参:在定义函数阶段定义的参数称为形式参数,相当于变量名

实参:在调用函数阶段传入的值称为实际参数,相当于变量值

只要能够得到一个值,都能够作为实参

#1:实参是常量
res=my_min(1,2)
#2:实参是变量
a=1
b=2
res=my_min(a,b)
#3:实参是表达式
res=my_min(10*2,10*my_min(3,4))
#4:实参可以是常量、变量、表达式的任意组合
a=2
my_min(1,a,10*my_min(3,4))

在调用阶段,实参(变量值)的内存地址会绑定给形参(变量名),这种绑定关系只在函数体内使用

实参与形参的绑定关系在函数调用时生效,函数调用结束后解除绑定关系

形参与实参的具体使用

位置参数

按从左至右的顺序依次定义的参数称为位置参数

# 位置形参
# 在函数定义阶段,按照从左至右的顺序直接定义的形参(变量名)
# 特点:必须按照位置被传值,多一个少一个都不行
def func(x,y):
    print(x,y)
# 位置实参
# 函数调用阶段,按照从左至右的顺序依次传入的值
# 特点:按照顺序与形参一一对应
func(1,2) # x=1 y=2
func(2,1) # x=2 y=1

关键字参数

关键字参数指的是关键字实参

# 在函数调用阶段,按照key=value的形式传值
# 特点:指名道姓给某个形参传值,完全不参照顺序
def func(x,y):
    print(x,y) # 1 2
func(y=2,x=1)

# 位置实参与关键字实参混用
# 位置实参必须放在关键字实参前
## func(y=2,1) # 语法错误
func(2,y=2)
# 不能为同一个形参重复传值
## func(1,y=2,x=3) # 错误
## func(1,x=3) # 错误

默认参数

默认参数指的是默认形参

# 在定义函数阶段,就已经被赋值的形参称为默认形参
# 特点:在定义阶段就已经被赋值意味着在调用阶段可以不赋值
# 位置形参与默认形参混用,强调:位置形参必须在默认形参的左边
def func(x,y=3):
    print(x,y)
func(1) # 1 3
func(1,2) # 1 2 # 也可以传值

# 默认参数的值是在定义阶段被赋值的,准确的说是被赋予的是值的内存地址
# 在python中所有值的传递都不是传递的值本身,而是值的内存地址
## 示范一
m = 2
def func1(x,y=m): # m=>2的内存地址 故 y=2的内存地址
    print(x,y)
m = 3 # m指向了3的内存地址
func1(1) # 1 2
## 示范二
# 虽然默认值可以指定为任意数据类型,但是不推荐使用可变类型
# 函数最理想的状态:函数的调用只跟函数本身有关系,不受外界代码的影响
m = [111,222]
def func1(x,y=m): # y指向了[111,222]的内存地址
    print(x,y)
m.append(333) # [111,222]的内存地址添加了333
func1(1) # 1 [111, 222, 333]

可变长度参数

可变长度指的是在调用函数时,传入的值(实参)的个数不固定,针对溢出部分的实参必须要有对应的新参来接收

可变长度的位置参数

# *形参名:用来接收溢出的位置实参,溢出的位置实参会被*保存成元组然后赋值给*形参名
# *形参名可以是任意名字,但args是默认的规范(推荐使用)
def func(x,y,*args):
    print(x,y,args)
func(1,2,3,4,5,6) # 1 2 (3, 4, 5, 6)

## 任意输入求和案例
def my_sum(*args): 
    sum = 0
    for i in args:
        sum = sum+i
    return sum
res = my_sum(1,2,3,4,5)
print(res)

# *用在实参中,是将值进行拆分,但是就需要形参实参数量相等了
def func(x,y,z):
    print(x,y,z)
func(*[1,2,3]) # 1 2 3
# 形参实参都带*
def func(x,y,*args):
    print(x,y,args)
func(1,2,[3,4,5]) # 1 2 ([3, 4, 5],)
func(1,2,*[3,4,5]) # 1 2 (3, 4, 5)

可变长度的关键字参数

# **形参名:用来接收溢出的关键字实参,**会将溢出的关键字实参保存成字典格式,然后赋值**形参名
## **形参名可以是任意名字,但kwargs是默认的规范(推荐使用)
def func(x,y, **kwargs):
    print(x,y,kwargs)
func(1,y=2,a=1,b=2,c=3) # 1 2 {'a': 1, 'b': 2, 'c': 3}

# **用在实参中(**后只能跟字典),是将值进行拆分,拆分成关键字实参
def func(x,y,z): # 必须一一对应
    print(x,y,z)
func(*{'x':'a','y':'b','z':'c'}) # x y z 使用位置参数会导致key被传入
func(**{'x':'a','y':'b','z':'c'}) # a b c

# 形参实参都带**
def func(x,y,**kwargs):
    print(x,y,kwargs)
func(**{'x':'a','y':'b','z':'c'}) # a b {'z': 'c'}
# 打散后 x=a,y=b,z=c 但是**kwargs溢出被保存为字典 {'z': 'c'}

可变关键字位置参数与可变关键字长度参数混用

简称*与**的混用(*args必须在**kwargs之前)

# 将wrapper的参数原封不动给了index
def index(x,y):
    print(x,y)
def wrapper(*args,**kwargs):
    # *args必须在**kwargs之前
    index(*args,**kwargs)
wrapper(1,y=2)

# 运行过程:
## 1.wrapper的*args接收到参数1,*args处理成元组args=(1,),**kwargs接收到参数处理成字典kwargs={'y':2}
## 2.调用index,函数变成了index(*(1,),**{'y':2})
## 3.index接收参数index(1,y=2)

命名关键字参数

在定义函数时,*后定义的参数,如下所示,称之为命名关键字参数

# 特点:命名关键字参数必须按照关键字传值
def func(x,y,*,a,b):
    # *或者*args后面的a,b被称为命名关键字参数
    print(x,y)
    print(a,b)
# func(1,2,3,4) # func() takes 2 positional arguments but 4 were given
func(1,2,a=1,b=2)

给命名关键字参数赋予默认值(注意:这里不叫默认参数)

def func(x,*,a=1,b):
    # 这里不是默认形参,所以a赋默认值可以在b前面
    print(x,a,b)
func(1,b=2) # 1 1 2

组合使用顺序

学习了这么多,我们组合使用的时候需要遵循一个最基本的先后顺序

# 定义顺序:位置形参、默认形参、*args、命名关键字形参、**kwargs
def func(x,y=111,*args,z,**kwargs):
    # z是命名关键字参数
    print(x,y,args,z,kwargs)

实参顺序

def func(x,y,z,a,b,c):
    print(x,y,z,a,b,c)
# func(111,y=222,*[333,444],**{'b':555,'c':666})
# 报错:func() got multiple values for argument 'y'
# 打散后:111,y=222,333,444,b=555,c=666
func(111,a=222,*[333,444],**{'b':555,'c':666}) # 本质还是形参位置问题 111 333 444 222 555 666
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值