学习python: 函数参数

大体上python中的参数可以根据其表现形式分为以下四种:

  1. 位置参数
  2. 默认参数
  3. 关键字参数
  4. 可变参数

其中前两种是比较常用的方式,其形式也c,c++等语言形式上比较相似,因此比较好理解。

关键字参数也是非常常用的方式,使用关键字参数不需要使传入的参数顺序和函数定义的参数顺序一致,因此更加灵活,尤其是参数过多的时候。

可变参数的形式用处也十分广泛,其表现形式有两种,分别是使用一个*和两个**来表示,且星号所处位置不同(分定义函数时和调用函数),其功能也有所差异。虽然这样,这种方式也不难理解,只需要多加练习即可。

在实际应用过程中,前三种方式最为常用,但是往往也会看到很多人使用后可变参数的方式进行程序的编写,因此学会并且掌握所有的参数的使用方法是十分必要的。

位置参数

位置参数和我们使用的c,c++等语言中定义和调用方法一致。且在调用函数的时候传入的参数顺序要和定义时候的顺序一致,其主要表现形式如下:

def f(num1, num2):
    print(num1)
    print(num2)

f(1, 2)

---
#Output:
1
2

由于位置参数比较简单,因此这里不在过多介绍。

默认参数

默认参数是指在我们定义函数的过程中,如果有一些参数大多数情况下都可以使用一个值,那么就可以给它一个默认的值,或者是给它一个初始的值。

这样的话当我再调用一个这个函数的时候,如果我给它传递一个值,那么函数就会收到我给它传递的值;否则,如果我没有给它传递值,那么函数也不会报错,而是使用默认的值作为参数。

举个例子:

def f(num, power=True):
    if power:
        print(num ** 2) 
    else:
        print(num)

f(2)                # 第一个参数由于是位置参数,因此必须传入值
                    # 但是第二个参数由于有了默认值,因此可以不给它传值,此时使用默认的值
f(2, True)          # 当然也可以给它附上一个值,注意此时没给它关键字信息,它会自动把`True`这个值匹配给函数中的power
f(2, False)         # 同上
f(2, power=False)   # 当然还可以使用`power=xxx`的方式去给这个参数传值
---
#Output
4
4
2
2 

关键字参数

上一节默认参数的最后,实际上已经用到了关键字参数的形式,f(2, power=False)power=False这样以一个key=value的形式来传递参数的方式即为关键字参数。和位置参数不同,关键字参数不需要使传入的参数顺序和定义的顺序相同,因为它是通过关键字去匹配的,如下面的例子:

def f(a, b, c):
    print('a = %d'%a)
    print('b = %d'%b)
    print('c = %d'%c)

f(1, 2, 3)		# 使用位置参数,1, 2, 3分别对应a, b, c
f(a=1, b=2, c=3)	# 使用关键字参数,也可以让传入参数顺序和定义顺序相同
f(b=2, c=3, a=1)	# 使用关键字参数,还可以使传入参数顺序和定义顺序不同
f(c=3, a=1, b=2)	# 同上

#Output
a = 1
b = 2
c = 3

a = 1
b = 2
c = 3

a = 1
b = 2
c = 3

a = 1
b = 2
c = 3

可变参数

一个*的表现形式(包裹位置参数)

所谓的包裹(packing)位置参数,就是在函数执行过程中,将传入的多余的位置参数打包为一个tuple传入包裹位置参数中。

一个经典的例子就是:如何将传递到函数里的参数全部相加,而且传入函数中的参数数量不确定。比如如果我传入1,2,那么就要输出3,如果输入1, 2, 3, 4,就是要输出10.

这时我们就可以使用可变参数来解决这个问题,其函数定义方式为f(*args)。注意,其中的args参数名字可以换为其它的名字,但是为了方便阅读和协作开发,我们通常使用args来表示一个可变参数。

这种形式当放在函数的定义中时,会把调用时多余的位置参数传递给args,并且封装为一个tuple。

我们可以通过如下的方式实现:

def f(*args):
    print(args)
    sum = 0
    for i in args:
        sum += i
    print(sum)

def f1(num, *args):
    print(args)
    sum = 0
    sum += num 
    for i in args:
        sum += i
    print(sum)

f()             # 可以看到,当可变参数什么都传递时,程序不会挂掉,而是在’args‘中收到一个空的tuple
f(1, 2)         # 当传入1,2时,函数接收到了两个参数到args中,并且形式为一个tuple
f(1, 2, 3, 4)   # 同上,只不过tuple中的元素个数变为了4个,即args = (1, 2, 3, 4)
print('---')
f1(1)           # 这个例子中,由于只传递了一个参数,而函数中又有位置参数,因此args中为空
f1(1, 2)        # 这个例子中, 由于有两个参数,因此把多余的2传递到args中,即args = (2)
f1(1, 2, 3, 4)  # 这个例子中, 由于多了三个参数,因此把多余的
                # 3个参数都给到args中,即args = (2, 3, 4)
---
#Output
()
0
(1, 2)
3
(1, 2, 3, 4)
10
---
()
1
(2,)
3
(2, 3, 4)
10

两个**的表现形式(包裹关键字参数)

所谓的包裹关键字参数,就是函数执行时将传入的多余关键字参数打包成一个dict,然后传递给一个固定的参数。

其函数定义的方式为f(**kwargs)。注意,其中参数kwargs的名字也是可以替换成任意名字的,只不过还是为了协作开发以及阅读的方便,我们约定俗成使用kwargs

举个例子:

def f(**kwargs):
    print(kwargs)

f(name = 'laowang', age = 30)	# 函数中没有位置参数,因此直接将关键字参数打包为一个字典传给kwargs,注意,我们在使用时不加两个`**`,直接使用`print(kwargs)`即可。

#Output 
{'age': 30, 'name': 'laowang'}
def f(name, age, **kwargs):
    print('name = %s' % name)
    print('age = %d' % age)
    print(kwargs)

f(name = 'laowang', age = 30)					# 当程序中有了位置参数时候,首先把传入的值给到位置参数
								# 如果没有多余的值,kwargs为空 
f(name = 'laoli', age = 30, name1 = 'huluwa', age1 = 80)        # 当程序中有了多余的关键字参数,才将多余的部分传递给kwargs

#Output
name = laowang
age = 30
{}

name = laoli
age = 30
{'age1': 80, 'name1': 'huluwa'}

解包

前面说道,***在函数中和调用时的使用方式是不一样的,我们已经知道在函数中他们起到打包(packing)的作用;自然可以推理一下在函数调用过程中它们应该是起到解包(unpacking)的作用。

首先看一个例子:

def f(*args, **kwargs):
    print(args)
    print(kwargs)
    print('\n')

a = (1, 2, 3)
b = {'name':'laowang', 'age':30}

f(1, 2, 3, name='laowang', age=30)	# 直接按照上两节讲的内容,将1,2,3打包成位置参数,将name和age打包成关键字参数
f(a, b)					# 这里由于a和b虽然其内容一个是tuple一个是dict,但是放在参数里都被当做了位置参数处理,而kwargs里面为空

# Output 
(1, 2, 3)
{'age': 30, 'name': 'laowang'}


((1, 2, 3), {'age': 30, 'name': 'laowang'})
{}

从上面的例子我们可以看出,我们希望让f(a, b)的调用方式能够和f(1, 2, 3, name='laowang', age=30)的调用方式得到相同的结果,但是却失败了。

原因是**kwargs只接受key=value形式的调用,而我们直接调用f(a, b), 虽然b={'name':'laowang', 'age':30}是一个字典,但是放在传入参数中时候b就变成了一个元素,因此没有达到我们想要的效果;变量a同理。

解决上述问题就是在调用时候增加一个解包(unpacking)的标识符即对应的***号。如将上述调用方式改为f(*a, **b),这样其效果就等同于调用f(1, 2, 3, name='laowang', age=30),也就是说,***号把对应的tuple或者dict中的元素一个一个解包出来了。

demo如下:

def f(*args, **kwargs):
    print(args)
    print(kwargs)
    print('\n')

a = (1, 2, 3)
b = {'name':'laowang', 'age':30}

f(1, 2, 3, name='laowang', age=30)
f(*a, **b)

# Output 
(1, 2, 3)
{'age': 30, 'name': 'laowang'}


(1, 2, 3)
{'age': 30, 'name': 'laowang'}

python 中参数的注意事项

一个需要注意的问题就是顺序问题,一个合法的顺序是位置参数->默认参数->包裹位置参数(*)->包裹关键字参数(**)。见下面的demo:

def f(a, b, c=10, *args, **kwargs):
    print(a)
    print(b)
    print(c)
    print(args)
    print(kwargs)

num = (1, 2, 3, 4)
info = {'name':'huluwa', 'grade':2}

f(100, 10, 20, *num, **info)

# Output 
100
10
20
(1, 2, 3, 4)
{'grade': 2, 'name': 'huluwa'}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章