Python函数的定义、函数的返回值、函数的参数

 

函数的定义

def 函数名(参数1,参数2,参数3,...):
    '''函数注释'''
    函数体
    return 返回的值

 

空函数

函数体代码为pass

def func(x, y):
    pass

 

调用函数

1、表达式形式

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


赋值表达式

res = add(1,2)
print(res)

 

数学表达式

res = add(1,2)*10
print(res)

 

2、语句的形式

只加括号调用函数

add(1,2)

 

3、函数调用可以当做参数

res = add(add(1,2),10)
print(res)

 
函数使用的原则

先定义,再调用。
函数即“变量”,“变量”必须先定义后引用。未定义而直接引用函数,就相当于在引用一个不存在的变量名。


测试一

def foo():
    print('from foo')
    bar()
foo() #报错

 

测试二

def bar():
    print('from bar')
def foo():
    print('from foo')
    bar()
foo() #正常

 

测试三

def foo():
    print('from foo')
    bar()
    
def bar():
    print('from bar')
foo() #正常

 

函数的返回值

1.return的作用:结束一个函数的执行

例如下面的例子:

def func():
    print (1111111)
    return#结束一个函数的执行
    print (1242451)
func4()

2.首先返回值可以是任意的数据类型。

 

3.函数可以有返回值。

如果有返回值,必须要用变量接收才有效果。
也可以没有返回值,没有返回值的时候分三种情况:
(1).当不写return的时候,函数的返回值为None
(2).当只写一个return的时候,函数的返回值为None
(3).return None的时候,函数的返回值为None(几乎不用)

函数有一个或多个返回值

1.不写return时返回None

def  func():
    a=111
    b=[1,2,3]

ret=func()
print(ret)


2.只写一个return时返回None

def  func():
    a=111
    b=[1,2,3]
    return
ret=func()
print(ret)

 

3.return None的时候返回None

def  func():
    a=111
    b=[1,2,3]
    return  None
ret=func()
print(ret)

 

4.return返回一个值(一个变量)

 

5.return返回多个值(多个变量) 
多个值之间用逗号隔开,以元组的形式返回。
接收:可以用一个变量接收,也可以用多个变量接收,返回几个就用几个变量去接收

方法一

def func():
    list=['hello','egon',11,22]
    return list[-1]

print(func())

方法二

def func():
    list=['hello','egon',11,22]
    return list

m,n,k,g=func()#
print(g)

 

函数的参数

形参与实参介绍

函数的参数分为形式参数和实际参数,简称形参和实参:
形参即在定义函数时,括号内声明的参数。形参本质就是一个变量名,用来接收外部传来的值。
实参即在调用函数时,括号内传入的值,值可以是常量、变量、表达式或三者的组合:

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))

 

在调用有参函数时,实参(值)会赋值给形参(变量名)。
在Python中,变量名与值只是单纯的绑定关系,而对于函数来说,这种绑定关系只在函数调用时生效,在调用结束后解除。

 

形参与实参的具体使用

位置参数

位置即顺序,位置参数指的是按顺序定义的参数,需要从两个角度去看:
在定义函数时,按照从左到右的顺序依次定义形参,称为位置形参,凡是按照这种形式定义的形参都必须被传值。

在调用函数时,按照从左到右的顺序依次定义实参,称为位置实参,凡是按照这种形式定义的实参会按照从左到右的顺序与形参一一对应。

def register(name,age,sex): #定义位置形参:name,age,sex,三者都必须被传值
    print('Name:%s Age:%s Sex:%s' %(name,age,sex))

 

如果调用函数的时候不指定任何参数明,则报错

register() #TypeError:缺少3个位置参数


关键字参数

在调用函数时,实参可以是key=value的形式,称为关键字参数,
凡是按照这种形式定义的实参,可以完全不按照从左到右的顺序定义,但仍能为指定的形参赋值

register(sex='male',name='lili',age=18)
# 输出结果: Name:lili Age:18 Sex:male

 

需要注意在调用函数时,实参也可以是按位置或按关键字的混合使用,
但必须保证关键字参数在位置参数后面,且不可以对一个形参重复赋值

register('lili',sex='male',age=18) #正确使用
register(name='lili',18,sex='male') #SyntaxError:关键字参数name=‘lili’在位置参数18之前
register('lili',sex='male',age=18,name='jack') #TypeError:形参name被重复赋值


默认参数

在定义函数时,就已经为形参赋值,这类形参称之为默认参数,
当函数有多个参数时,需要将值经常改变的参数定义成位置参数,而将值改变较少的参数定义成默认参数。

例如编写一个注册学生信息的函数,如果大多数学生的性别都为男,那完全可以将形参sex定义成默认参数

def register(name,age,sex='male'): #默认sex的值为male
    print('Name:%s Age:%s Sex:%s' %(name,age,sex))

 

定义时就已经为参数sex赋值,意味着调用时可以不对sex赋值,这降低了函数调用的复杂度

大多数情况,无需为sex传值,默认为male

register('tom',17)

 输出结果:

Name:tom Age:17 Sex:male

 

少数情况,可以为sex传值female

register('Lili',18,'female') 

 输出结果:

Name:Lili Age:18 Sex:female


需要注意:
默认参数必须在位置参数之后
默认参数的值仅在函数定义阶段被赋值一次

x=1
def foo(arg=x):
    print(arg)

x=5 #定义阶段arg已被赋值为1,此处的修改与默认参数arg无任何关系
foo()

 输出结果:

1


默认参数的值通常应设为不可变类型

虽然默认值可以被指定为任意数据类型,但是不推荐使用可变类型。
函数最理想的状态:函数的调用只跟函数本身有关系,不外界代码的影响。

def foo(n,arg=[]):    
     arg.append(n)    
     return arg    
foo(1)

 输出结果:    

[1] 

 

foo(2) 

 输出结果:   

[1, 2] 

 

foo(3)

输出结果:  

[1, 2, 3]

如上范例是一个可变的列表,所以arg 指向的是列表的内存地址,arg在定义阶段被赋予的是一个内存地址,而这个内存地址的值是一直变化的。

 

每次调用是在上一次的基础上向同一列表增加值,修改如下

def foo(n,arg=None):    
     if arg is None:    
         arg=[]    
     arg.append(n)    
     return arg    
     
foo(1) 

输出结果:   

[1] 

 

foo(2)  

输出结果:  

[2] 

 

foo(3) 

 输出结果:  

[3]


可变长度的参数(与*的用法)

参数的长度可变指的是在调用函数时,实参的个数可以不固定,
而在调用函数时,实参的定义无非是按位置或者按关键字两种形式,
这就要求形参提供两种解决方案来分别处理两种形式的可变长度的参数


可变长度的位置参数

如果在最后一个形参名前加号,那么在调用函数时,溢出的位置实参,都会被接收,以元组的形式保存下来赋值给该形参

def foo(x,y,z=1,*args): #在最后一个形参名args前加*号
    print(x)
    print(y)
    print(z)
    print(args)

foo(1,2,3,4,5,6,7)  

 

实参1、2、3按位置为形参x、y、z赋值,
多余的位置实参4、5、6、7都被*接收,以元组的形式保存下来,赋值给args,即args=(4, 5, 6,7)
输出结果:

1
2
3
(4, 5, 6, 7)

 

如果我们事先生成了一个列表,仍然是可以传值给*args的

def foo(x,y,*args):
    print(x)
    print(y)
    print(args)

L=[3,4,5]
foo(1,2,*L)

*L就相当于位置参数3,4,5, foo(1,2,*L)就等同于foo(1,2,3,4,5)
输出结果:

1
2
(3, 4, 5)


注意:如果在传入L时没有加*,那L就只是一个普通的位置参数了

仅多出一个位置实参L

foo(1,2,L)

输出结果:

1
2
([1, 2, 3],)

 

如果形参为常规的参数(位置或默认),实参仍可以是*的形式

def foo(x,y,z=3):
    print(x)
    print(y)
    print(z)

foo(*[1,2]) #等同于foo(1,2)

输出结果:

1
2
3

 

形参与实参中都带*

def func(x,y,*args):
    print(x,y,args)

func(1,2,[3,4,5,6])
func(1,2,*[3,4,5,6]) # 相当于 func(1,2,3,4,5,6)
func(*'hello') # 相当于 func('h','e','l','l','o')

输出结果:

1 2 ([3, 4, 5, 6],)
1 2 (3, 4, 5, 6)
h e ('l', 'l', 'o')

 

如果我们想要求多个值的和,*args就派上用场了

def add(*args):
    res=0
    for i in args:
        res+=i
    return res

add(1,2,3,4,5)

输出结果:

15

可变长度的关键字参数

如果在最后一个形参名前加号,那么在调用函数时,溢出的关键字参数,都会被接收,以字典的形式保存下来赋值给该形参

def foo(x,**kwargs): #在最后一个参数kwargs前加**
    print(x)        
    print(kwargs)   

foo(y=2,x=1,z=3) 

溢出的关键字实参y=2,z=3都被**接收,以字典的形式保存下来,赋值给kwargs
输出结果:

1
{'z': 3, 'y': 2}


如果我们事先生成了一个字典,仍然是可以传值给**kwargs的

def foo(x,y,**kwargs):
    print(x)
    print(y)
    print(kwargs)

dic={'a':1,'b':2} 
foo(1,2,**dic)

**dic就相当于关键字参数a=1,b=2,foo(1,2,**dic)等同foo(1,2,a=1,b=2)
输出结果:

1
2
{'a': 1, 'b': 2}


注意:如果在传入dic时没有加**,那dic就只是一个普通的位置参数了

foo(1,2,dic) #TypeError:函数foo只需要2个位置参数,但是传了3个


如果形参为常规参数(位置或默认),实参仍可以是**的形式

def foo(x,y,z=3):
    print(x)
    print(y)
    print(z)

foo(**{'x':1,'y':2})  # 等同于foo(y=2,x=1)

输出结果:

1
2
3

 

可以在在实参中带*,注意对比下面输出的不同

def func(x,y,z):
    print(x,y,z)

func(*{'x':1,'y':2,'z':3}) # 相当于 func('x','y','z')
func(**{'x':1,'y':2,'z':3}) # 相当于 func(x=1,y=2,z=3)

 

输出结果:

x y z
1 2 3

 

下面为错误示范

func(**{'x':1,'y':2,}) # 相当于 func(x=1,y=2)
func(**{'x':1,'a':2,'z':3}) # 相当于 func(x=1,a=2,z=3)

 

如果我们要编写一个用户认证的函数,起初可能只基于用户名密码的验证就可以了,
可以使用**kwargs为日后的扩展供良好的环境,同时保持了函数的简洁性。

def auth(user,password,**kwargs): 
    pass 

 

命名关键字参数

在定义了**kwargs参数后,函数调用者就可以传入任意的关键字参数key=value,
如果函数体代码的执行需要依赖某个key,必须在函数内进行判断

def register(name,age,**kwargs):
    if 'sex' in kwargs:
        #有sex参数
        pass
    if 'height' in kwargs:
        #有height参数
        pass

 

想要限定函数的调用者必须以key=value的形式传值,
Python3提供了专门的语法:需要在定义形参时,用作为一个分隔符号,之后的形参称为命名关键字参数。
对于这类参数,在函数调用时,必须按照key=value的形式为其传值,且必须被传值

def register(name,age,*,sex,height): #sex,height为命名关键字参数
	pass

register('lili',18,sex='male',height='1.8m') #正确使用
register('lili',18,'male','1.8m') # TypeError:未使用关键字的形式为sex和height传值
register('lili',18,height='1.8m') # TypeError没有为命名关键字参数height传值。


命名关键字参数也可以有默认值,从而简化调用

def register(name,age,*,sex='male',height):
	print('Name:%s,Age:%s,Sex:%s,Height:%s' %(name,age,sex,height))

register('lili',18,height='1.8m')

输出结果:

Name:lili,Age:18,Sex:male,Height:1.8m

 

需要强调的是:sex不是默认参数,height也不是位置参数,因为二者均在后,
所以都是命名关键字参数,形参sex='male'  属于命名关键字参数的默认值,因而即便是放到形参height之前也不会有问题。
另外,如果形参中已经有一个args了,命名关键字参数就不再需要一个单独的*作为分隔符号了

def register(name,age,*args,sex='male',height):
	print('Name:%s,Age:%s,Args:%s,Sex:%s,Height:%s' %(name,age,args,sex,height))

register('lili',18,1,2,3,height='1.8m') #sex与height仍为命名关键字参数

输出结果:

Name:lili,Age:18,Args:(1, 2, 3),Sex:male,Height:1.8m


组合使用

综上所述所有参数可任意组合使用,但定义顺序必须是:位置参数、默认参数、args、命名关键字参数、*kwargs

可变参数*args与关键字参数kwargs通常是组合在一起使用的,*args必须在**kwargs之前。
如果一个函数的形参为*args与kwargs,那么代表该函数可以接收任何形式、任意长度的参数

def wrapper(*args,**kwargs):
    pass

 

在该函数内部还可以把接收到的参数传给另外一个函数

def func(x,y,z):
    print(x,y,z)

def wrapper(*args,**kwargs):
    func(*args,**kwargs)

wrapper(1,z=3,y=2)

输出结果:

1 2 3

 

按照上述写法,在为函数wrapper传参时,其实遵循的是函数func的参数规则,调用函数wrapper的过程分析如下:

位置实参1被接收,以元组的形式保存下来,赋值给args,
即args=(1,),关键字实参z=3,y=2被*接收,以字典的形式保存下来,赋值给kwargs,即kwargs={'y': 2, 'z': 3}
执行func(args,kwargs),即func((1,),* {'y': 2, 'z': 3}),等同于func(1,z=3,y=2)

提示: *args、**kwargs中的args和kwargs被替换成其他名字并无语法错误,但使用args、kwargs是约定俗成的。

 

posted @ 2022-11-10 22:50  屠魔的少年  阅读(7)  评论(0)    收藏  举报