Python函数、参数、可变参数、keyword-only

Python

Python函数、参数、可变参数、keyword-only

函数

  • 数学定义:y = f(x),y是x的函数,x是自变量。y = f(x0,x1,…,xN)
  • Python函数:
    函数是由若干语句组成的语句块、由函数名称、参数列表构成,它是组织代码的最小单元
    可以完成特定的功能
    

函数的作用

  1. 结构化编程对代码的最基本的封装,按照功能组织一段代码
  2. 封装的目的是为了代码的复用,减少冗余代码
  3. 使代码更加简洁美观、且易读性更强

函数的分类

  • 内建函数; 如 max()、len()等
  • 库函数:如 math.ceil() 等

函数的定义

使用def关键字 定义函数

1
2
3
def 函数名(参数列表):
函数体(代码块)
[return 返回值]

  • 函数名就是标识符,命名要求一样
  • 语句块必须缩进,约定4个空格
  • Python的函数没有return语句,函数会隐式的返回一个None值
  • 定义中的参数列表会成为形式参数,它只是符号表达,简称形参

函数的调用

  • 函数的定义,只是声明了一个函数,但是它不会自动执行,需要被调用才能执行
  • 调用的方式,就是函数名加上小括号,括号内写上参数
  • 调用时写的参数就是实际参数,是实实在在传入的值,简称实参

函数的举例

1
2
3
4
5
6
def add(x,y):
result = x+y
return result

out = add(4,5)
print(out)
  • 上面只是一个函数的定义,有一个函数叫做add,接收2个参数
  • 计算的结果,通过返回值返回
  • 通过调用函数名add加上2个参数,返回值可使用变量接收
  • 定义需求在调用前,也就是说调用时,已经被定义过,否则抛出NameError异常
  • 函数是可调用的对象,callable()

参数

  • 函数形参的作用是实现主调函数与被调函数之间的联系,通常将函数所处理的数据,影响函数功能的因素或者函数处理的结果作为形参
  • 参数调用时传入的参数要和定义的个数相匹配(可变参数除外)

位置参数

  • 按照参数定义的顺序传入的参数就是位置参数
    如:
1
2
3
def f(x,y,z):
pass
f(1,2,3) # 1,2,3 按照位置参数的定义赋值给 x,y,z

关键字参数

  • 使用形参的名字传入函数中,如果使用的使用形参的名字,那么传参的顺序就和定义的顺序不一样了
    如:
1
2
3
def f(x,y,z):
pass
f(x=1,y=2,y=3) # 按照关键字参数的定义,直接使用形参的名字进行赋值

传参案例

1
2
3
f(z=None, y=10, x=[1])
f((1,), z=6, y=4.1)
f(y=5, z=6, 1) # SyntaxError: positional argument follows keyword argument
  File "<ipython-input-3-27c4e4027067>", line 3
    f(y=5, z=6, 1)   #
               ^
SyntaxError: positional argument follows keyword argument

总结: 位置参数必须在关键字参数之前传入,位置参数按照位置对应

参数默认值(缺省值)

  • 定义形参时,可以在形参后跟上一个值
    如:
1
2
3
4
5
6
7
8
9
10
11
12
def add(x=4,y=5):
return x + y
add(4,5)
add(x=4,y=5)
add(y=4,x=5)
add(4,y=5)
# add(4,x=5) # got multiple values for argument 'x'
add(x=5)
add(y=4)
add()
# add(x=4,5) # positional argument follows keyword argument
# add(y=4,5) # positional argument follows keyword argument
9

总结:

  • 参数的默认值可以在未传入足够的实参时候,对没有给定的参数赋值为默认值
  • 参数非常多的时候,并不需要用户每次都输入所有的参数,简化函数调用
  • 位置参数必须在关键字参数之前
  • 不能给同一个参数赋多个值

函数默认参数案例:

1
2
3
4
def login(host='127.0.0.1',port='8080',username='lpx',password='123456'):
print('host:{}\nport:{}\nusername:{}\npassword:{}'.format(host,port,username,password))

login()
host:127.0.0.1
port:8080
username:lpx
password:123456

可变参数

位置可变参数

无可变参数出现的问题:如果实参有100个,实现这一百个数的求和

1
2
3
4
5
6
7
8
9
def f(nums):
sumnum = 0
for i in nums:
sumnum += i
return sumnum

lst = [x for x in range(1,101)]
print(f(lst))
print(f(1,2,3))
5050



---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-18-c97b6b4b48cc> in <module>
      7 lst = [x for x in range(1,101)]
      8 print(f(lst))
----> 9 print(f(1,2,3))


TypeError: f() takes 1 positional argument but 3 were given

说明默认一个位置形参只能接收一个实参,所以list接收了,f(1,2,3)无法接收,报错为给定参数过得多,导致连函数体都无法进入

可变参数:

1
2
3
4
5
6
7
8
def add(*nums):
sum = 0
print(type(nums))
for x in nums:
sum += x
print(sum)

add(3, 6, 9) # 将多个参数封装成元组传入
<class 'tuple'>
18

总结:

  • 一个形参可以匹配多个实参
  • 只需在形参前使用 *表示该形参为可变参数,可接收多个实参
  • 本质上是将收集到的多个实参放在一个tuple中

可变关键字参数

  • 在形参前使用 ** ,表示可以接收多个关键字实参
  • 收集到的关键字实参的名称和值组成一个字典
    案例:
1
2
3
4
5
6
def showconfig(**kwargs):
print('type:{} **kwargs:{}'.format( type(kwargs),kwargs ))
for k,v in kwargs.items():
print(k,v)

showconfig(host='127.0.0.1',port='8080',username='lpx',password='123456')
type:<class 'dict'> **kwargs:{'host': '127.0.0.1', 'port': '8080', 'username': 'lpx', 'password': '123456'}
host 127.0.0.1
port 8080
username lpx
password 123456

可变参数总结

  • 可变参数分为可变位置参数和可变关键字参数
  • 位置可变参数在形参前使用一个星号 *
  • 关键字可变位置参数在形参前使用两个星号 **
  • 位置可变参数和关键字可变参数都可以收集多个实参
  • 位置可变参数收集到的实参形成一个tuple,关键字可变位置参数收集到的实参形成一个dict
  • 在混合使用参数时,普通参数放在参数列表前面,关键字可变参数放在列表后面,位置可变参数行中间

keyword-only参数

1
2
3
4
def fn(*,x):
print(x)

fn(x=4)
4
  • keyword-only参数在Python3加入
  • 如果在一个星号参数后,或者一个位置可变参数后,出现的普通参数,实际上已经不是普通参数了,而是keyword-only参数
  • 从语法上卡args可以看做已经截获了所有的位置参数,x不使用关键字参数就不可能拿到实参
  • keyword-only参数必须给赋值,若不赋值则报错

案例演示:

1
2
3
4
5
6
def fn(*args,x,y):
print(x)
print(args)

fn(3,5) # TypeError: fn() missing 1 required keyword-only argument: 'x'
fn(3,5,x=7,y=10) # 正确的写法
7
(3, 5)

由以上注释代码可知: args可以看做是截获了所有的位置参数,x不使用关键词传参就拿不到实参,如果x拿不到实参就会报错

keyword-only默认值

  • 不管什么类型的位置参数,只要有默认值都会保存在函数的 defaults 属性中
1
2
3
4
5
6
7
def fn(*args,x=1,y=2):
print(args)
print(x,y)

fn()
fn(1,2,x=3,y=4)
fn.__defaults__
()
1 2
(1, 2)
3 4
  • 能否定义fn(*kwargs,x)?
    根据传参方式
    * kwargs 通过关键字传参,x也得通过关键字传参,x的关键词传参有可能会被关键字可变参数吸收,所以关键字可变参数不能定义在keyword-only前
    如:
1
2
3
4
5
6
# def fn(**kwargs,x):   # invalid syntax  错误
def fn(x,**kwargs):
print(x)
print(kwargs)

fn(x=2,y=5,z=1)
2
{'y': 5, 'z': 1}

函数参数定义规范

  • 函数参数列表的一般顺序为:普通位置参数、默认(缺省值)参数、可变位置参数、keyword-only(可带缺省值)参数、可变关键字参数
  • 代码应该简单易懂,而不是为了为难被人,尤其是在项目中不要给自己和他人挖坑,要做到复杂问题简单化
  • 书写函数参数时,应该按照标准规范定义函数参数
    如:
1
2
3
4
def fn(x,y,z=3,*args,m=4,n,**kwargs):
print(x,y,z,m,n)
print(args)
print(kwargs)

函数传参方式

位置参数传参

  • def f(x,y,z) 调用使用f(1,3,5)
  • 按照参数定义顺序传入实参

关键字参数传参

  • def f(x,y,z) 调用使用f(x=1,y=3,z=5)
  • 使用形参的名字来传入实参,如果使用形参名字,那么传参的顺序就可和定义的顺序不同了

参数解构

  • 给函数提供实参的时候,可以在集合类型前使用 * 或者 ** ,把集合类型的结构解开,提取里面的元素作为函数的实参
  • 非字典类型使用 * 解构成位置参数
  • 字典类型使用 ** 解构成关键字参数
  • 提取出来的元素数目要和参数的要求匹配,也要和参数的类型匹配
    如:
1
2
3
4
5
6
def add(x,y):
return x+y
print(add(*(4,5)))
print(add(*[4,5]))
print(add(*{4,5}))
print(add(**{'x':4,'y':5}))
9
9
9
9

传参总结

函数传参的方式有两种

  1. 通过位置参数传参
  2. 通过关键字参数传参
  • 通常位置参数在前,关键字参数在后,若采用关键字参数时,顺序可以迭代,但是位置参数传入时,顺序不能打乱
-------------本文结束感谢您的阅读-------------
0%