函数
引入
什么是函数?
函数就是一种工具, 可以重复使用
为什么要用函数?
防止代码冗余和增强代码的可读性
怎么用函数?
先定义后使用
函数的定义规范
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 被 * 接收, 以元组的形式保存下来, 赋值给 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
函数入门
来源: http://www.bubuko.com/infodetail-3295366.html