函数入门

函数

引入

什么是函数?

函数就是一种工具,可以重复使用

为什么要用函数?

防止代码冗余和增强代码的可读性

怎么用函数?

先定义后使用

函数的定义规范

def 函数名(参数1,参数2...):
	'''文档描述'''
    函数体
    return 值
#主要由关键字def+函数名(尽量为有意义的)+():组成

def:定义函数的关键字

函数名:函数名指向函数内存地址,是对函数体代码的引用。函数名一般为有意义的,可以反映出函数的功能

函数名:命名规范与变量名命名规范一致
1、一定不要与关键字冲突
2、一定要见名知意

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

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

文档描述:描述函数功能,参数介绍等信息的文档,非必要,但建议加上,从而增强函数的可读性

函数体:由语句和表达式组成了

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

函数的类型

无参函数

def login(): #括号里面无参数
    print('登陆成功')

有参函数

username = input('请输入你的用户名:')
def login(username):
	print(username)

空函数

def login():  #一般常用在编写多功能程序时构建项目框架,用pass充当函数体占位符
    pass

调用函数与函数返回值

函数的使用分为定义阶段与调用阶段,定义函数时只检测语法,不执行函数体代码

不写return

#不写return默认返回None
def login():
    print('success')


print(login())
#结果为
success
None

只写return:只有结束函数体代码的作用,默认返回None

写return None:与只写return效果一样

return返回一个值:可以将返回的结果,当作一个变量使用

return返回多个值:1 将返回的多个值存入一个元组

​ 2 返回值不想被修改

​ 3 可以指定返回的数据类型

形参与实参介绍

形参即在定义函数时,括号内声明的参数。形参本质就是一个变量名,用来接收外部传来的值。

实参即在调用函数时,括号内传入的值,值可以是常量、变量、表达式或三者的组合,实参本质就是一个变量值

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

函数的传参方式

位置参数:按照参数位置去传参
def register(name, age, sex):  # 定义位置形参:name,age,sex,三者都必须被传值    		print('Name:%s Age:%s Sex:%s' % (name, age, sex))
    
    
register('sean', 66, 'boy')  #按照从左往右的先后顺序将值传给对应的参数
#输出结果为
Name:sean Age:66 Sex:boy
关键字参数:指定参数进行传值,与参数位置无关
def register(name, age, sex):
    print(f'Name:{name}, Age:{age}, Sex:{sex}')


register(age=73, name='tank', sex='male')
#输出结果为
Name:tank, Age:73, Sex:male

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

def register(name, age, sex):
    print('Name:{1},Age:{0}, Sex:{2}'.format(age, name, sex))


register(age=73, name='sean', sex='male')
register(sex='male', 18, name='sean')
register('tank', sex='male', age=18, name='sean')
#输出结果为
Name:sean,Age:73, Sex:male
SyntaxError: positional argument follows keyword argument
TypeError: register() got multiple values for argument 'name' 

默认参数

def register(name, age, sex='male'):
    print('名字:{}, 年龄:{}, 性别:{}'.format(name,age,sex))


register('sean', 27) 
register('tank', 73, sex='female')
#输出结果为
名字:sean, 年龄:27, 性别:male  #这里我们在定义函数时,就设置了默认参数sex='male'
名字:tank, 年龄:73, 性别:female #因为我们自己设定了传参的值

#PS:
#	 默认参数必须在位置参数之后
#    默认参数的值仅在函数定义阶段被赋值一次
#    如果在实参的时候传入了一个新的参数,就会使用新参数
#    默认参数在传值时,不要将可变类型当作参数传递
    
#每次调用都是在上一次的基础上向同一列表增加值
def foo(n,arg=None):
    if arg is None:
        arg = []
        arg.append(n)
        print(arg)
foo(1)
foo(2)
foo(3)
#输出结果为
[1]
[2]
[3]

可变长参数

可变长位置参数

#如果在最后一个形参名前加*号,那么在调用函数时,溢出的位置实参,都会被接收,以元组的形式保存下来赋值给该形参
#   *args:接受所有溢出的位置参数;接受的值都被存入一个元组
def foo(a, b, c, *args):
    print(a, b, c, *args)  #这里写为*args的话相当于直接将其当成一个正常的参数来用;也可以理解为*将默认得到的元组拆分了
	print(a, b, c, args)   

foo(1, 2, 3, [4, 5, 6])
#输出结果为
1 2 3 [4, 5, 6]
1 2 3 ([4, 5, 6],)   #默认将溢出的值存入一个元组


def foo(a, b, c, *args):
    print(a, b, c, args)
    print(a, b, c, *args)


foo(1, 2, 3, 4, 5, 6)
#输出结果为
1 2 3 (4, 5, 6)
1 2 3 4 5 6
#PS:两个对比我们可以得到,*号的作用打散你传入的容器类型


#求和小练习
def sum(*args):
    a = 0
    for i in args:
        a += i
    print(a)
    

sum(10, 11, 12)  

可变长关键字参数

#如果在最后一个形参名前加**,那么在调用函数时,溢出的关键字参数,都会被接收,以字典的形式保存下来赋值给该形参
def foo(a, **kwargs):  #在最后一个参数kwargs前加**
    print(a, kwargs)
	print(a, *kwargs)

foo(b=1, a=2, c=3)    #溢出的关键字实参b=1,c=3都被**接收,以字典的形式保存下来,赋值给kwargs
#输出的结果为
2 {'b': 1, 'c': 3}
2 b c                #字典打散则取key


def foo(x, y, **kwargs):
    print(x, y, kwargs)
    
    
dic = {'a':1, 'b':2}
foo(1, 2, **dic)
#输出结果为
1 2 {'a': 1, 'b': 2}
#PS:
#实参可以直接定义字典传值给形参
#形参为常规参数时,实参可以是**的形式

命名关键字参数

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

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


register('sean', 18, sex = 'male')
#输出结果为
...

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

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


register('lili',18,sex='male',height='1.8m') #正确使用

#命名关键字参数可以有默认值
def register(name,age,*,sex='male',height):
    print('Name:{},Age:{},Sex:{},Height:{}' .format(name, age, sex, height))


register('tank', 18, height='1.8m') #这里我们没有传入sex的参数
#输出的结果为
#需要强调的是:sex不是默认参数,height也不是位置参数,因为二者均在*后,所以都是命名关键字参数,形参sex=’male’属于命名关键字参数的默认值,因而即便是放到形参height之前也不会有问题。另外,如果形参中已经有一个args了,命名关键字参数就不再需要一个单独的*作为分隔符号了

def index(name, age, *args, sex = 'male', height):
    print(f'Name:{name}, Age:{age}, Args:{args}, Sex:{sex}, Height:{height}')


index('sean', 18, 1, 2, 3, height='170')
#输出结果为
Name:sean, Age:18, Args:(1, 2, 3), Sex:male, Height:180

综上所述所有参数可任意组合使用,但定义顺序必须是:位置参数、默认参数、*args、命名关键字参数、**kwargs
可变参数args与关键字参数**kwargs通常是组合在一起使用的,如果一个函数的形参为args与**kwargs,那么代表该函数可以接收任何形式、任意长度的参数
def func(x,y,z):
    print(x,y,z)
def index(*args,**kwargs):
    func(*args, **kwargs)


index(x=1,y=2,z=3)  #这里传的值可以是全为关键字参数或全为位置参数,或者混合使用
#该函数执行的大概流程为定义函数func和index,然后调用函数index,而函数index中的代码块为执行func函数,()中的参数*args和**kwargs即为上面要输出的x,y,z

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

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

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

原文地址:https://www.cnblogs.com/a736659557/p/11892349.html