Python函数的参数类型以及参数传递是传值还是传址

Python函数中的参数

在定义函数的时候将参数的名字和位置都确定,这样便是把函数的接口完成。
Python中的函数定义简单、灵活,除了正常定义的必选参数,还可以使用默认参数、可变参数和关键字参数等,以便定义出的接口可以处理复杂的参数和方便调用者调用函数。

下面便总结一下Python中函数的几种参数:位置参数、默认参数、可变参数、关键字参数和命名关键字参数。

位置参数(必备参数)

当调用参数时,根据参数的位置进行传参,例如:计算x的n次方

def fun(x,n):
  n = n-1
  while n:
    x *= x
    n = n-1
  return x

此处进行传参时,就需要根据x和n的位置传入参数,所以,x和n也就是位置参数。在C++语言中的函数参数一般就是位置参数。
当参数定义为位置参数时,传参时就需要注意参数位置不能错啊。
位置参数也称为必备参数,在调用时的传入的数量必须和位置要和声明时的一样。

默认参数

Python中的默认参数类似于C++中函数的默形参值,Python中可以为形参指定默认值。
其中必备参数在前,默认参数在后,与C++中默认性参值规则一样。

但是,这里需要注意一个大坑,也是上一篇“可变对象与不可变对象”中说过的当函数默认参数为可变对象时,不传入参数调用函数,会改变函数的默认形参值。
在编程时要尽量将对象设计为不可变对象。

可变参数(参数收集)

当参数个数不确定时,可以考虑将参数作为tuple或者list传入。

>>> def cal(num):
...     sum=0
...     for n in num:
...         sum += n
...     return sum
...
>>> cal([1,2,3])
6
>>> cal((4,5,6))
15


这里还有一种方法,在Python中,在参数名前加一个*号便是定义一个可变参数,这样便实现对参数的“收集”,并且在函数内部,收集到的参数将是一个tuple。

>>> def cal(*num):
...     print('*num=',num)
...     sum = 0
...     for n in num:
...         sum += n
...     return sum
...
>>> print('cal(1,2,3,4,5,6)=',cal(1,2,3,4,5,6))
*num= (1, 2, 3, 4, 5, 6)
cal(1,2,3,4,5,6)= 21

*号代表的就是收集其余位置参数。如果没有任何提供收集的参数,那么other_params将是一个空元组

>>> def test(first_param,*other_params):
...     print(first_param)
...     print(other_params)
...     
>>> test(1)
1
()
>>> test(1,2,3)
1
(2, 3)


但是,出现了一种情况,此时的参数就是tuple或者list,要调用cal(*num)可变参数该怎么办?

可以在tuple或者list前面加一个*号,把tuple或list的元素变成可变参数传进去。

>>> def cal(*num):
...     sum = 0
...     for n in num:
...         sum += n
...     return sum
...
>>> nums = (1,2,3,4)
>>> cal(*nums)
10

个人觉得这种用法体现了*号用在形参前是起参数收集作用,用在传入的实参前是起分散参数的作用即参数收集的逆过程。若理解偏差,可批评指出。

关键字参数

关键字参数顾名思义就是传参时带上参数名表示出传入了什么参数的值。
关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。

前面介绍了 * 号可以进行对其余位置参数收集,那对关键字参数进行“收集”则需要**

>>> def test(name,age,**otherinfo):
...     print("name:",name,"age:",age,"otherinfo:",otherinfo)
...     
>>> test(name='Sakura', age='20')
name: Sakura age: 20 otherinfo: {}
>>> test(name='Sakura', age='20',city='Hangzhou')
name: Sakura age: 20 otherinfo: {'city': 'Hangzhou'}


和上面可变参数类似,可以事先组装出一个dict然后把dict转换为关键字参数传入,此时需要在dict前面加上**

>>> def test(name,age,**otherinfo):
...     print("name:",name,"age:",age,"otherinfo:",otherinfo)
...
>>> extrainfo={'city':'Chengdu','job':'Teacher'}
>>> test('Sakura',20,**extrainfo)
name: Sakura age: 20 otherinfo: {'city': 'Chengdu', 'job': 'Teacher'}

需要注意,这里的otherinfo获得的是extrainfo的拷贝

命名关键字参数

当要限制关键字参数的名字时,就可以使用命名关键字参数。
命名关键字参数需要一个特殊分隔符 后面的参数被视为命名关键字参数。参数列表中若无*,则将定义为位置参数


比如,只接收city和job作为关键字参数。

>>> def person(name, age, *, city, job):
...     print(name,age,city,job)
...     
>>> person('Sakura',20,city='Hangzhou',job='Teacher')
Sakura 20 Hangzhou Teacher


关键字参数必须要传入参数名,若没有传入参数名,会报错,被理解为位置参数。

>>> person('Sakura',20,'Hangzhou','Teacher')
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: person() takes 2 positional arguments but 4 were given


其中*的位置可以是可变参数,后面的关键字参数可以由默认值,若有默认值,则可以不用传入

>>> def person(name, age, *params, city='Hangzhou', job):
...     print(name,age,city,job)
...     
>>> person('Sakura',20,job='Teacher')
Sakura 20 Hangzhou Teacher

函数参数传递是传值还是传址?

讲了这么多Python参数的类型,那么进行参数传递时,到底是传的是值,还是址呢?
这就涉及到前面的一篇“可变对象和不可变对象”中介绍的一切传递都是对象的引用。看下面例子,认识Python函数参数传递。

不可变对象参数调用

>>> def fun(param):
...     print(param)
...     param = 2
...     
>>> num = 1
>>> fun(num)
1
>>> num
1

这里有一个值为1的整型不可变对象,传入fun()函数,因为在Python中一切传递都是对象的引用,所以param也是对整型对象1的引用。因为整型对象1为不可变对象,所以在函数内部是创建了另外一个值为2的整型对象并让param指向这个对象。
所以,对不可变对象参数的调用可以看做是C++中const型的引用,当然,也可以理解为传值。

可变对象参数调用

>>> def fun(param):
...     param[0] = 'changed'   
...     return param
...
>>> str=['1','2','3']
>>> fun(str)
['changed', '2', '3']
>>> str
['changed', '2', '3']
>>> print(id(str),' ',id(fun(str)))
2079552186248   2079552186248

list为可变对象,传入函数时,对其做的改变就是改变对象本身,也就是相当于C++中的引用。

猜你喜欢

转载自blog.csdn.net/renzhadebenyuan/article/details/79340852