【python】详解python函数定义 def()与参数args、可变参数*args、关键参数**args使用实例

时间:2021-08-25 21:25:54

Python内置了很多函数,可以直接调用。Python内置的函数可以通过官方文档查看。也可以通过help()查看帮助信息。函数名是指向函数对象的引用,把函数名赋给变量,相当于给函数起了别名。

# 变量a为函数abs()的别名
In [1]: a = abs

In [2]: a(1)
Out[2]: 1

1. 定义函数

  • 使用def 函数名(参数):语句定义函数,在缩进块中编写函数体,返回值用return语句返回,如果没有return语句,返回值为None,等价于return None
  • 根据需要,return语句也可以返回多个值组成的tuple,可以不写括号(),多个值之间用逗号’,’隔开,调用函数时可以用多个变量接受返回值,多个变量按位置赋值
In [1]: #自定义取绝对值函数,根据大于0返回输入值,小于0返回相反值
   ...: def my_abs(x):
   ...:     #isinstance()检查参数类型
   ...:     if not isinstance(x, (int, float)):
   ...:         raise TypeError('bad operand type')
   ...:     if x >= 0:
   ...:         return x
   ...:     else:
   ...:         return -x

In [2]: my_abs(1)
Out[2]: 1

In [3]: my_abs(-1)
Out[3]: 1
  • pass语句可以用做占位符,表示什么都不做,可用来定义一个空函数。在没想好具体语句的时候,可以用pass语句使代码先运行起来
#定义空函数
In [6]: def asshole(x):
   ...:     pass
   ...:
#此时不会有结果
In [7]: asshole(5)

2. 默认参数:用于定义函数,为参数提供默认值,调用函数时可传可不传该默认参数的值(注意:所有位置参数必须出现在默认参数前,包括函数定义和调用)

  • 将变化小的参数作为默认参数,如果没有传入或改变默认参数的值,则使用默认参数。如果不需要改变默认参数,不用传入默认参数;需要改变默认参数时,传入默认参数即可
  • 必选参数在前,默认参数在后。有多个默认参数时,调用的时候,既可以按顺序提供默认参数,也可以不按顺序提供部分默认参数。当不按顺序提供部分默认参数时,需要把参数名写上。表示该参数用传进去的值,其他默认参数继续使用默认值

实例:学生注册信息,设置姓名和性别为必选参数,设置年龄和城市为默认参数

#设置姓名和性别为必选参数,设置年龄和城市为默认参数
In [9]: def enroll(name, gender, age = 7, city = 'Beijing'):
   ...:     print('name = ', name, '\tgender = ', gender, '\tage = ', age, '\tcity = ', city)
   ...:
#如果不需要改变默认参数,不用传入默认参数,必选参数一定要传入,且在默认参数的前面
In [10]: enroll('Jim', 'M')
name =  Jim     gender =  M     age =  7        city =  Beijing
#需要改变默认参数时,传入默认参数即可
In [11]: enroll('Lily', 'F', 6, 'Tianjin')
name =  Lily    gender =  F     age =  6        city =  Tianjin
#不要要改变的默认参数不用传入;如果没有传入默认参数的名称,则按照默认参数的顺序进行传递
In [12]: enroll('Tom', 'M', 8)
name =  Tom     gender =  M     age =  8        city =  Beijing
#如果传入默认参数的名称,即改变该默认参数的值
In [13]: enroll('Jack', 'M', city = 'Xi\'an')
name =  Jack    gender =  M     age =  7        city =  Xi'an
  • 默认参数必须指向不变对象。因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。
#定义一个添加的函数,默认参数是空列表[]
In [16]: def add_end(L = []):
    ...:     L.append('end')
    ...:     return L
    ...:
#改变默认参数[]为[1,2,3],则在[1,2,3]添加'end'
In [17]: add_end([1, 2, 3])
Out[17]: [1, 2, 3, 'end']
#改变默认参数[]为['a', 'b', 'c'],则在['a', 'b', 'c']添加'end'
In [18]: add_end(['a', 'b', 'c'])
Out[18]: ['a', 'b', 'c', 'end']
#在默认空列表中添加'end',得到['end']
In [19]: add_end()
Out[19]: ['end']
''' Python函数在定义时,默认参数的值已经被计算出来。因为列表为可变对象,L指向可变对象,每次调用函数时,如果改变了L的内容,下次调用时,默认参数的内容就变了,不是函数定义时的列表了。 '''
In [20]: add_end()
Out[20]: ['end', 'end']

In [21]: add_end()
Out[21]: ['end', 'end', 'end']

由于列表是可变对象,为了固定默认参数的值,函数add_end()可做如下修改,用不可变对象None实现不改变默认参数的值:

In [25]: def add_end(L = None):
    ...:     if L is None:
    ...:         L = []
    ...:     L.append('end')
    ...:     return L
    ...:

In [26]: add_end()
Out[26]: ['end']

In [27]: add_end()
Out[27]: ['end']

3. 可变参数

  • 传入的参数个数可变,可以为0到任意个在参数前加*实现可变参数。 可变参数在函数调用时自动组装为一个tuple
  • 列表和元组前加*,可以将列表或元组中的元素变为可变参数传入函数。
#列表和元组前加*,实现可变参数的传递
In [29]: def add_sum(*nums):
    ...:     sum = 0
    ...:     for num in nums:
    ...:         sum += num
    ...:     return sum
    ...:

#直接传入数个参数
In [30]: add_sum(1, 2, 3)
Out[30]: 6
#列表和元组前加*,实现可变参数的传递
In [33]: L = [1, 2, 3, 4]
In [34]: add_sum(*L)
Out[34]: 10

In [35]: T = (1, 2)
In [36]: add_sum(*T)
Out[36]: 3

4. 关键字参数

  • 将任意个含参数名的参数组装为一个dict。在参数前加**实现关键字参数
  • 在dict前加**,可以将dict作为为关键字参数传入函数。传入的为dict的拷贝,在函数中的修改不会影响函数外的dict。

实例:定义函数person()包含必选参数name和age,还包含关键字参数kw:

#定义person函数,返回函数的各个参数值
In [37]: def person(name, age, city = 'Beijing', country = 'china', **kw):
    ...:     print('name: ', name, '\tage: ', age, '\tcity: ', city, '\tcountry: ', country, '\tother: ', kw)
    ...:
#定义关键字参数dict字典other
In [38]: other = {'gender': 'F', 'height': 168}
    ##当没有给出关键字参数
    ...: person('Tom', 18)
    ...:
name:  Tom      age:  18        city:  Beijing  country:  china         other:  {}
#当单独给出必选参数和默认参数时,喂给函数的参数没有事先定义,则当做关键字参数给入
#gender = 'F', hobby = 'read'由于事先没有定义,所以当做关键字参数定义
In [39]: person('Lily', 20, city = 'Tianjin', gender = 'F', hobby = 'read')
    ...: person('Tim', 15, gender = 'M', city = 'Beijing')
    ...:
name:  Lily     age:  20        city:  Tianjin  country:  china         other:  {'hobby': 'read', 'gender': 'F'}
name:  Tim      age:  15        city:  Beijing  country:  china         other:  {'gender': 'M'}
##将dict作为关键字参数直接传入
In [40]: 
    ...: person('Ann', 16, **other)
name:  Ann      age:  16        city:  Beijing  country:  china         other:  {'height': 168, 'gender': 'F'}

5. 命名关键字参数

  • 关键字参数可以传入任意多个名字,对名字没有限制。
    如果要限制关键字参数的名字,需要在参数列表中使用之后的关键字为命名关键字,只接受这些参数作为关键字名字。
  • 如果已经有可变参数,不需要特殊分隔符*
In [41]: def person(name, age, *, city, country):
    ...:     print('name: ', name, '\tage: ', age, '\tcity: ', city, '\tcountry: ', country)
    ...: #如果已经有可变参数,不需要特殊分隔符*
    ...: def person1(name, age, *args, city, country):
    ...:     print('name: ', name, '\tage: ', age, '\tcity: ', city, '\tcountry: ', country)
    ...:

调用函数时,必须传入参数名,位置可以颠倒:

In [42]: person('Tom', 18, city = 'Tianjin',country = 'china')
    ...: person('Tom', 18, country = 'china', city = 'Tianjin')
    ...: person1('Tom', 18, city = 'Tianjin',country = 'china')
    ...: person1('Tom', 18, country = 'china', city = 'Tianjin')
    ...:
name:  Tom      age:  18        city:  Tianjin  country:  china
name:  Tom      age:  18        city:  Tianjin  country:  china
name:  Tom      age:  18        city:  Tianjin  country:  china
name:  Tom      age:  18        city:  Tianjin  country:  china

该函数只接受两个位置参数,如果调用时不用函数名person(‘Tom’, 18, ‘Tianjin’, ‘china’), Python解释器认为传入了四个位置参数,会报错:

In [43In [43]: person(‘Tom’, 18, ‘Tianjin’, ‘china’)
  File "<ipython-input-43-48ab073e2d8c>", line 1
    person(‘Tom’, 18, ‘Tianjin’, ‘china’)
               ^
SyntaxError: invalid character in identifier

如果命名关键字参数有缺省值,调用时可以不传入该参数

6. 组合参数

  • 不同类型的函数可以组合使用,参数定义的顺序必须为:必选【位置】参数,默认参数,可变参数,命名关键字参数和关键字参数
#a,b为必选参数,c为默认参数,d为可变参数,e为命名关键字参数,kw为关键字参数
In [44]: def func1(a, b, c = 0, *d, e, **kw):
    ...:     print(a, '\t', b, '\t', c, '\t', d, '\t', e, '\t', kw)
    ...:
    ...: def func2(a, b, c = 0, *, d, e, **kw):
    ...:     print(a, '\t', b, '\t', c, '\t', d, '\t', e, '\t', kw)
    ...:
    ...: def func3(a, b, c):
    ...:     print(a, '\t', b, '\t', c)
    ...:

定义变量:

In [45]: t = (3, 4, 5, 6, 7, 8, 9)
    ...: l = [3, 4, 5, 6, 7, 8, 9]
    ...: t1 = (4, 5, 6)
    ...: dic = {'name': 'Z', 'gender': 'M'}

输出:

#定义a、b为必选参数,c默认参数,*d可变参数,e命名关键字参数,**kw关键字参数
In [44]: def func1(a, b, c = 0, *d, e, **kw):
    ...:     print(a, '\t', b, '\t', c, '\t', d, '\t', e, '\t', kw)
In [46]: print('func1')
#a = 1,b = 2,c = 0,*d需要传入列表或者元组,e = 4命名关键字参数,出现给e的赋值都返回在关键字参数e的值下;d = 3作为关键字参数传入
In [48]: func1(1, 2, e = 4, d = 3)
1        2       0       ()      4       {'d': 3}

In [49]: func1(1, 2, d = 3, e = 4)
1        2       0       ()      4       {'d': 3}

In [50]: func1(1, 2, d = 3, e = 4, name = 'L', gender = 'F')
1        2       0       ()      4       {'name': 'L', 'd': 3, 'gender': 'F'}

In [51]: func1(*t, e = 10)
3        4       5       (6, 7, 8, 9)    10      {}

In [52]: func1(*l, e = 10)
3        4       5       (6, 7, 8, 9)    10      {}

In [53]: func1(*t, e = 11, **dic)
3        4       5       (6, 7, 8, 9)    11      {'name': 'Z', 'gender': 'M'}

In [54]: func1(*l, e = 11, **dic)
3        4       5       (6, 7, 8, 9)    11      {'name': 'Z', 'gender': 'M'}

func1中:
- d为可变参数,可以为空,d=3被认为是关键字参数;
- e为命名关键字参数,调用时必须使用名字调用;
- 用*t, *l, *dic调用时,*t, *l的前三个元素作为必选参数a, b, 默认参数c,后面的部分作为命名关键字e = 10,剩下的部分作为可变参数构成tuple d = (6, 7, 8, 9), **dic作为关键字参数。

In [55]:print('func2')
    ...:func2(1, 2, d = 3, e = 4)
    ...:func2(1, 2, d = 3, e = 4, name = 'L', gender = 'F')
    ...:func2(*t1, d = 20, e = 20)
    ...:func2(*t1, d = 21, e = 21, **dic)
    ...:func3(*t1)
func2
1        2       0       3       4       {}
1        2       0       3       4       {'name': 'L', 'gender': 'F'}
4        5       6       20      20      {}
4        5       6       21      21      {'name': 'Z', 'gender': 'M'}
4        5       6

func2中:
- d, e均为命名关键字,必须使用名字调用。
- 因为不包含可变参数,如果要使用tuple, list调用,tuple和list的长度必须不超过固定参数加默认参数的长度,所以只能使用t1进行调用。