python基础梳理(十)函数function

一、基础概念
函数是什么:
函数是解决问题的方案,可以重复调用的语句块。
作用:
1.解决实际问题
2.用于封装可重复执行的语句,提高语句的可重用性
3.定义用户级别的函数

函数定义语句 :def
语法:
def 函数名(参数列表):
语句块(代码块)

函数的语法说明:
1.函数的名字就是语句块的名称
2.函数命名规则与变量的命名规则相同
3.函数名是一个变量(不要轻易对其赋值)
4.函数有自己的命名空间,再函数内部可以访问外部的变量,但是外部的语句不能访问函数内部的变量
在这里插入图片描述
在这里插入图片描述

5.函数如果不需要传入参数,则参数列表可以为空
6.语句部分不能为空,如果需要位空需要填充pass语句

示例:实现一个空函数
def test():
pass #如果函数什么也不做,必须写pass 这一点也和c区别

函数调用:
函数名(实际调用传递参数)
注:实际调用传递参数 简称“实参”
调用说明:
1.函数调用是一个表达式
2.如果没有return语句,函数执行完后返回None对象
3.如果函数需要返回其他的对象,需要用到return语句

示例:对于一个没有return语句的函数,返回的是None
在这里插入图片描述

示例:函数的定义、函数的调用以及参数的传递

初学者需要注意:x,y是形式参数 m和n是实际传递的参数,叫实参
在这里插入图片描述
在这里补充几点:
1.python中我们比较一个字符串不用像c++一样,可能要去用模板啊,或写好多重载啊,python一个函数所有类型通用,python变量没有类型说法,拿来用就行。
2.全局变量和局部变量。全局变量是定义在函数外部的,所有函数都可以访问全局变量,我们有时需要用del删除全局变量,但是对于局部变量一般不需要,除了函数的作用域会自动消失。

return 语句:
语法:
return [表达式]
注:[]中的内容可以省略
作用:
用于函数中,结束当前函数的执行,返回到函数调用的地方,同时返回一个对象的引用关系
说明:
1.return语句后跟的表达式可以省略,省略后相当于return
2.如果函数内没有return语句,则函数执行完最后一条语句后返回None,相当于在最后加了一条return None 语句

python中return语句可以同时返回多个变量吗?
答:可以
在这里插入图片描述
python中的return语句可以返回列表等序列码?
答:可以
在这里插入图片描述

二、传参方式:参数的个数由形参个数决定,多了不行,少了也不行

1.位置传参:实际调用参数(实参) 的对应关系与形式参数(形参)的对应关系时是按位置来一次对应的。(对应到c中实际上是实参栈帧和形参栈帧的对应关系,这里不展开说)
示例:
在这里插入图片描述

2.序列传参:是用在函数调用过程中的,用 * 将序列拆解后按位置进行传递的传参方式。实参和形参通过序列传递和匹配( 星号在c中代表解引用,就是将内存中中值取出来,这里是代表将序列拆开然后依次传入 )
示例:
在这里插入图片描述
在这里插入图片描述

但是如果序列中的元素多于形参怎么办:报错 一切以形参的个数为准
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

补充:打印 s = “ABCD” 可以使用 * 来拆解
print ( * s )
3.关键字传参: 传参时,按着形参的名称给形参赋值(即进行名称的匹配)
在这里插入图片描述
在这里插入图片描述

4.字典关键字传参:实参为字典,用 ** 拆解字典后再进行关键字传参
说明:
1.字典的键值名必须和形参名一致
2.字典的键名必须为字符串
3.字典的键名要在形参中存在
示例:
在这里插入图片描述
在这里插入图片描述

综合传参:
函数的传参方式在能确定形参能唯一匹配到相应实参的情况下可以任意组合
注:
通常位置传参和序列传参先传递,其次是关键字传参和字典关键字传参
示例:
在这里插入图片描述
在这里插入图片描述
三、函数形参
1.函数的缺省参数
语法:
def 函数名(形参名1 = 默认实参1,形参名2 = 默认实参2 …):
语句
示例:
def info(name,age=1,address = “不详”):
print(“我叫”,name,“我今年”,age,‘岁’,“家住”,address)
info(“张飞”,100,“北京”)
info(“刘备”,99)
info(“李白”)
定义默认参数的规则:
def fx(name,age = 1,address) #erro
必须自右向左依次存在
缺省参数可以有0个,1个‘多个,甚至都有

缺省参数的绑定对象存在于函数中,和函数的生命周期一致

正确的示范:

def info1(name = "葫芦娃", age =1 , address="不详"):
    print("我叫", name, "我今年", age, '岁', "家住", address)

info1("张飞", 100, "北京")
info1("刘备", 99)
info1("李白")
info1()     #三个形参都有默认值,那么实参可以一个都不给

def info2(name, age =1 , address="不详"):
    print("我叫", name, "我今年", age, '岁', "家住", address)

info2("张飞", 100, "北京")
info2("刘备", 99)
info2("李白")
#info2()    默认形参没有给name默认值,我们实参也不给  必然会出错


def info3(name, age , address="不详"):
    print("我叫", name, "我今年", age, '岁', "家住", address)

info3("张飞", 100, "北京")
info3("刘备", 99)
#info3("李白") 默认形参没有给age默认值,我们实参也不给  必然会出错
#info2()      默认形参没有给name默认值,我们实参也不给  必然会出错


#如果形参给了默认值,实参没有给就用默认值,如果实参也给了,就用实参的

错误的示例:

def info4(name = "葫芦娃", age , address="不详"):
    print("我叫", name, "我今年", age, '岁', "家住", address)

info4("张飞", 100, "北京")
info4("刘备", 99)
info4("李白")
info4()     

def info5(name = "葫芦娃", age=1 , address):
    print("我叫", name, "我今年", age, '岁', "家住", address)

info5("张飞", 100, "北京")
info5("刘备", 99)
info5("李白")
info5()  

#一定要从右向左**依次**给定默认参数

练习题:
示例一:

def fn(a,list = []):
    list.append(a)
    print(list)
L = [1,2,3,4]

fn(5,L)
fn(6,L)


结果:
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]

示例二:

def fn(a,list = []):
    list.append(a)
    print(list)
L = [1,2,3,4]

fn(1.1)
fn(2.2)
fn(3.3)

结果:
[1.1]
[1.1, 2.2]
[1.1, 2.2, 3.3]

为什么之后的函数调用会沿用之前的函数中的形参变量
???
在这里插入图片描述
python的变量在命名的时候就会给分配,而c++除了全局变量其余是在运行的时候才会给开辟内存空间,对于c++中的函数,内存是运行才会给分配,不区分运行前自身的内存和运行时栈帧这部分内存
python中一切皆对象,包括带默认值的函数中的形参默认值,也是一个对象,也会有自己的内存区域,这块内存区域属于函数执行前“自带的”
再等到函数被调用时,系统又会给函数中局部变量分配运行时的内存,包括函数中局部变量等,直到函数运行结束,这块内存又会被回收。

如图所示:
在这里插入图片描述
当第二次去调用fn(2.2),依旧没有给定第二个默认值,所以第二次调用延用之前第一调用的那个list,即函数体内生成的那个list对象

def fn1(a,lst = []):
    lst.append(a)
    print("------------------------")
    print(lst)
    print(id(lst))
L = [1,2,3,4]

fn1(1.1)
fn1(2.2,L)
fn1(3.3)

执行结果:
[1.1]
2297844075912
------------------------
[1, 2, 3, 4, 2.2]
2297844182344
------------------------
[1.1, 3.3]
2297844075912

从上述结果可以很清楚看出,默认值参数使用的是同一个list对象,只要是带默认值的,就会被延用,如果我们给定list,那就是另外一个对象另外一块内存啦。

**四、函数的形参定义方式:**形参:接收传来的用户传来的实参
1.位置形参

def	函数名(形参名1,形参名2....)
		语句块
#按照位置对应关系接收

2.星号元组形参

def		函数名(*元组形参名)
			语句块
作用:收集多余的位置传参
示例:
def fun(*arg):
    print("用户传来的参数个数为:",len(arg))
    print("用户传来的参数有    :", arg)

fun(1,2,3,4)
fun()


执行结果:
用户传来的参数个数为: 4
用户传来的参数有    : (1, 2, 3, 4)
用户传来的参数个数为: 0
用户传来的参数有    : ()



#arg绑定的是个元组,有了这个,位置形参的个数可以不定,然后我们可以通过操作元组来操作形参

max函数的实现机制:就是利用元组传参
print(max(1,2))
print(max(1,2,3,4))
print(max("hello","hei","python"))
执行结果:
2
4
python

3.命名关键字形参
语法:
def 函数名(*,命名关键字形参):
语句块
或者
def 函数名 (*arg,命名关键字形参)

作用:
所有的命名关键字形参都强制调用者采用关键字传参或字典关键字传参的方式传递
示例:

def fun1(a,*,k):
    print("a = ",a)
    print("k = ",k)
#  *在这里起分隔的作用,该函数实际上只有俩个参数,a和k
# ,其中*右边必须以关键字传参的方式进行传参
#错误示例     fun1(100,200)
#右边的必须以关键字传参的方式进行
fun1(100,k = 1000)

执行结果:
a =  100
k =  1000

一个错误的示例:
def fun2(a,*arg,k,f,h):
    print("a = ",a)
    print("k = ",k)
    print("arg = ",arg)
    print("f = ",f)
    print("h = ",h)
fun2(100,200,300,400,500)
运行结果:
TypeError: fun2() missing 3 required keyword-only arguments: 'k', 'f', and 'h'
分析:
a =100 ,遇到*arg会将实参中传来的所有位置参数打包起来arg = (200,300,400,500)
由于*右边需要关键字传参,所以k f h都匹配不上
修改:
fun2(100,200,300,400,500,f = 999,h =999999,k = 0)
运行结果:
a =  100
k =  0
arg =  (200, 300, 400, 500)
f =  999
h =  999999

4.双星号字典形参
语法:
def 函数名(**字典形参名)
语句块
作用:
收集多余的关键字传参
注:
字典形参名通常叫kwargs

示例:

def fun3(**kwargs):
    print("关键字传参的个数是:",len(kwargs))
    print("kwargs = ",kwargs)
    
    fun3(name = "hello",length = 199)
    执行结果:
    关键字传参的个数是: 2
    kwargs =  {'name': 'hello', 'length': 199}
    
    fun3(a = 100,b = 200,c = 300,d = [1,2,3],e = True)
    执行结果:
    关键字传参的个数是: 5
    kwargs =  {'c': 300, 'a': 100, 'b': 200, 'd': [1, 2, 3], 'e': True}

五、函数参数说明:
位置形参、缺省参数,星号元组形参、命名关键字形参、双星号字典形参可以混合使用
混合使用的规矩函数参数自左向右的顺序为
1.位置形参
2.星元组形参
3.命名关键字形参
4.双星号形参

示例:

def fun5(a,b,*arg,c,**kwargs):
    print("a = ",a)
    print("b = ", b)
    print("arg = ", arg)
    print("c = ",c)
    print("kwargs = ", kwargs)

fun5(100,200,300,400,c = 1000,name = "python",length = "6")

执行结果:
a =  100
b =  200
arg =  (300, 400)
c =  1000
kwargs =  {'name': 'python', 'length': '6'}



fun5(100,200,300,400,*'ABc',**{'d':'D'},c = 9999)
执行结果:
a =  100
b =  200
arg =  (300, 400, 'A', 'B', 'c')
c =  9999
kwargs =  {'d': 'D'}

六、可以接收任意位置传参和关键字传参的函数

语法:
def  fun(*args,**kwargs):
		pass


def anyfun(*args,**kwargs):
    print("args = ",args)
    print("kwargs = ",kwargs)

anyfun(100,'A',200,300,a = 100,c = 200)
执行结果:
args =  (100, 'A', 200, 300)
kwargs =  {'a': 100, 'c': 200}

七、练习
1.实现一个可变参函数Sum,功能:返回所有参数之和

def Sum(*args):
    sum = 0
    for i in args:
        sum +=i
    return sum
ret =Sum(1,2,3,4,5)
print(ret)

执行结果:
15

2.实现一个Max函数:要求可变参,返回所有参数中最大值

def Max(*args):
    max = args[0]
    for i in args:
        if i >= max:
            max = i
    return max
ret = Max(1,2,3,4,5,99,0,9999,7)
print(ret)

执行结果:
9999

猜你喜欢

转载自blog.csdn.net/KingOfMyHeart/article/details/88841984
今日推荐