您现在的位置是:首页 > 学无止境 > 其他网站首页其他 Python函数的进阶
Python函数的进阶
- 其他
- 2019-07-26
简介上一节我们学习了函数的定义和调用,理解了基本的函数知识。本节进一步学习函数相关的更多内容,深入了解函数,包括:默认参数、关键字参数、位置参数、变量的作用域等等。字数
3044
上一节我们学习了函数的定义和调用,理解了基本的函数知识。本节进一步学习函数相关的更多内容,深入了解函数,包括:默认参数、关键字参数、位置参数、变量的作用域等等。
形参和实参的不同
首先,我们先搞清两个概念:
形参(parameters),是定义函数时声明的参数名称,它定义了函数可以接受的参数类型;
实参(arguments),是调用函数时传给函数的实际值。
比如下面的函数定义:
def func(foo, bar=True, **kwargs): pass
foo, bar, kwargs 是函数的形参(parameters)。
当我们调用func
时,比如:
func(20, bar=False, abc='xyz')
传给函数的值20
,False
和'xyz'
就是实参。
默认参数值
默认参数就是在函数定义时,给参数一个默认值。调用该函数时可以不给有默认值的参数传值,这样调用时的参数可以减少。
看下面的例子:
def say_hi(name, greeting='Hi', more=''): print(greeting, name, more)
这个函数有三个参数,其中greeting
和more
有默认值,调用该函数时,有以下几种形式:
- 只传一个值给name: say_hi(‘Tom’)
- 给可选参数greeting传值: say_hi(‘Tom’, ‘Hello’)
- 给所有参数传值: say_hi(‘Tom’, ‘Hello’, ‘how are you’)
一个函数可以有任意多个默认参数,也可以全部参数都有默认值。但是,没有默认值的参数不能在有默认值的参数的后面,否则会报错。也就是说,参数的先后顺序必须是无默认值参数在前,有默认值参数在后面。
In [175]: def foo(a=1, b): ...: return a + b File "<ipython-input-175-f8e0e97b520a>", line 1 def foo(a=1, b): ^SyntaxError: non-default argument follows default argument
默认参数值只生成一次,如果默认值是可变对象比如list、dict、set等就会出现诡异的结果,使用时要非常留心。下面的例子,默认值为一个空list,函数调用时它把第一个参数放到list里面。
def func(a, L=[]): L.append(a) return Lprint(func(1))print(func(2))print(func(3))
程序的输出是:
[1][1, 2][1, 2, 3]
这是因为默认值L在函数定义时生成,后面的调用(使用默认值,不给L传值)就会不断给定义时生成的默认list添加元素。
如果你希望使用这个诡异的特性也没问题,但要清楚它是什么。通常我们不建议默认值为可变对象,而是不可变的整数、浮点数、字符串等等。
关键字参数
调用函数时,我们可以使用“关键字参数”,它的形式是:kwarg=value
。比如前面的say_hi
函数的调用:
In [180]: say_hi(name='Tom')Hi Tom In [181]: say_hi(name='Tom', greeting='Hello')Hello Tom In [182]: say_hi(name='Tom', more='how are you')Hi Tom how are youIn [183]: say_hi(more='good day', name='Tom', greeting='Hi')Hi Tom good day
上面最后一个调用告诉我们,关键字参数是通过关键字来确认参数的,所以可以不用按照函数定义时的顺序传递参数。
关键字参数跟默认参数类似有参数位置的限制,关键字参数后面必须都是关键字参数。
下面的调用都是无效的:
say_hi() # 缺少必须的参数namesay_hi(name='Tom', 'Hello') # 关键字参数后面出现了非关键字参数say_hi('Tom', name='Tim') # 同样的参数传了两个值say_hi(age=10) # 函数定义中不存在的关键字参数
如果函数定义的最后一个参数是两个星号加名称:**name
,那么它接受一个字典包含所有关键字参数,这个字典不包括name前面声明的普通参数:
In [190]: def foo(a, **kwargs): ...: print(a) ...: for k, v in kwargs.items(): ...: print('%s : %s' % (k, v)) ...:In [191]: foo(1)1In [192]: foo(a=1)1In [193]: foo(a=1, b=2, c=3, d=4, e='a')1b : 2c : 3d : 4e : a
可见,**kwargs
把a后面的所有关键字参数都接收了。这对我们以后写参数非常多的函数时很有帮助。
可变参数列表
可变参数列表类似关键字参数**kwargs
,因为它没有key只有value,所以它是一个序列(确切的说是一个tuple),它的形式是*args
,名称前面有一个星号*
,用以接收不确定数量的参数。我们常用的内置函数print
就是一个可变参数函数。
下面我自己定义一个可变参数函数:
In [197]: def foo(*args): ...: print(type(args)) ...: print('|'.join(args)) ...:In [198]: foo('a', 'b', 'c', 'd')<class 'tuple'>a|b|c|d
同样的,可变参数后面必须跟关键字参数:
In [204]: def foo(*args, joiner='|'): ...: print(type(args)) ...: print(joiner.join(args)) ...:In [205]: foo('a', 'b', 'c', 'd')<class 'tuple'>a|b|c|dIn [206]: foo('a', 'b', 'c', 'd', joiner='/')<class 'tuple'>a/b/c/d
解包参数列表
这个正好跟可变参数列表相反,如果要调用的函数的参数值已经在list或tuple里面了,我们可以通过解包list或tuple来给函数传值。比如内置的range()
函数可以输入两个参数:start和stop,如果它们在一个llist或tuple里面,可以通过*
操作符解包来传值:
In [207]: list(range(1, 8))Out[207]: [1, 2, 3, 4, 5, 6, 7]In [208]: args = [1, 8]In [209]: list(range(*args))Out[209]: [1, 2, 3, 4, 5, 6, 7]
同样的,dict可以通过**
操作符传递关键字参数:
In [212]: d = {'name':'Tom', 'greeting':'Hello', 'more':'good day'}In [213]: say_hi(**d)Hello Tom good day
lambda函数
通过关键字lambda
来实现小的匿名函数。匿名函数返回一个函数对象,在可以作为函数参数传递给函数。比如内置函数sorted
中的key
参数就接受一个函数对象。
In [215]: func = lambda a, b: a * bIn [216]: func(1,2)Out[216]: 2In [217]: func(3,5)Out[217]: 15
再看看sorted使用lambda函数的情况,先对学生按姓名排序,再按年龄排序:
In [218]: students = [ ...: {'name': 'Tom', 'age': 12}, ...: {'name': 'Jack', 'age': 13}, ...: {'name': 'Aby', 'age': 10},]In [219]: sorted(students, key=lambda s: s['name'])Out[219]: [{'name': 'Aby', 'age': 10}, {'name': 'Jack', 'age': 13}, {'name': 'Tom', 'age': 12}]In [220]: sorted(students, key=lambda s: s['age'])Out[220]: [{'name': 'Aby', 'age': 10}, {'name': 'Tom', 'age': 12}, {'name': 'Jack', 'age': 13}]
变量的作用域和生命周期
程序中的变量不是在任何地方都可见的,它有自己的作用域。
局部变量
定义在函数内部的变量只在函数内部可见,也就是说,它是函数的局部变量。
In [1]: def func(): ...: x = 'hello' ...: print(x) ...:In [2]: func()helloIn [3]: x------------------------NameError Traceback (most recent call last)<ipython-input-3-6fcf9dfbd479> in <module>----> 1 xNameError: name 'x' is not defined
x是func
内部的一个变量,对该函数内部可见,所以print(x)
语句能打印x的值。但是在函数外部访问x时就会报错:x是未定义的。
全局变量
相对于局部变量,全局变量是定义在函数外部的,它具有全局作用域。
In [4]: x = 'hello'In [5]: def func2(): ...: print(x) ...:In [6]: func2()helloIn [7]: xOut[7]: 'hello'
如果要在函数内部修改全局变量,就需要用关键字global
来声明全局变量:
In [8]: def func3(): ...: global x ...: x += 'world' ...: print(x) ...:In [9]: func3()helloworldIn [10]: xOut[10]: 'helloworld'
局部变量变量的生命周期从函数调用开始,到函数运行结束为止;全局变量的生命周期直到整个程序结束为止。
删除函数
前面的章节中,我们使用关键字del
来删除列表或其中的元素,它同样可以用来删除函数:
In [11]: def func4(): ...: print('func4') ...:In [12]: func4()func4In [13]: del func4In [14]: func4()-------------------NameError Traceback (most recent call last)<ipython-input-14-0e6ad11a93c1> in <module>----> 1 func4()NameError: name 'func4' is not defined
在Python中,函数也是对象,所以用del
删除函数就跟删除其它对象一样。
文档字符串(docstring)
作为类、函数或模块之内的第一个表达式出现的字符串字面值。它在代码执行时会被忽略,但会被解释器识别并放入所在类、函数或模块的 doc 属性中。由于它可用于代码内省,因此是对象存放文档的规范位置。
In [15]: def my_func(): ...: '''first line is summary of this function ...: ...: the more lines are details of this function ...: ''' ...: pass ...:In [16]: print(my_func.__doc__)first line is summary of this function the more lines are details of this function
写docstring的规则一般是这样的:
(1)第一行简短概述该函数或类的功能
(2)第二行空白
(3)后面几行详细描述函数的参数、返回值等等
总结
定义函数时,参数称为“形参”,表述参数的类型;调用函数时,参数为“实参”,是传给函数的具体值。
定义函数时,可以为参数指定默认值;调用函数时,可以通过关键字参数调用。
定义函数是,可以指定参数为可变参数列表 *args
或**kwargs
;调用函数时,可以通过解包list,tuple和dict来传入参数。
转载:
感谢您对莫愁个人博客网站平台的认可,非常欢迎各位朋友分享到个人站长或者朋友圈,但转载请说明文章出处“来源莫愁个人博客 https://www.mochoublog.com/study/289.html”。
- 其他
- 2019-07-26
上一节我们学习了函数的定义和调用,理解了基本的函数知识。本节进一步学习函数相关的更多内容,深入了解函数,包括:默认参数、关键字参数、位置参数、变量的作用域等等。
形参和实参的不同
首先,我们先搞清两个概念:
形参(parameters),是定义函数时声明的参数名称,它定义了函数可以接受的参数类型;
实参(arguments),是调用函数时传给函数的实际值。
比如下面的函数定义:
def func(foo, bar=True, **kwargs): pass
foo, bar, kwargs 是函数的形参(parameters)。
当我们调用func
时,比如:
func(20, bar=False, abc='xyz')
传给函数的值20
,False
和'xyz'
就是实参。
默认参数值
默认参数就是在函数定义时,给参数一个默认值。调用该函数时可以不给有默认值的参数传值,这样调用时的参数可以减少。
看下面的例子:
def say_hi(name, greeting='Hi', more=''): print(greeting, name, more)
这个函数有三个参数,其中greeting
和more
有默认值,调用该函数时,有以下几种形式:
- 只传一个值给name: say_hi(‘Tom’)
- 给可选参数greeting传值: say_hi(‘Tom’, ‘Hello’)
- 给所有参数传值: say_hi(‘Tom’, ‘Hello’, ‘how are you’)
一个函数可以有任意多个默认参数,也可以全部参数都有默认值。但是,没有默认值的参数不能在有默认值的参数的后面,否则会报错。也就是说,参数的先后顺序必须是无默认值参数在前,有默认值参数在后面。
In [175]: def foo(a=1, b): ...: return a + b File "<ipython-input-175-f8e0e97b520a>", line 1 def foo(a=1, b): ^SyntaxError: non-default argument follows default argument
默认参数值只生成一次,如果默认值是可变对象比如list、dict、set等就会出现诡异的结果,使用时要非常留心。下面的例子,默认值为一个空list,函数调用时它把第一个参数放到list里面。
def func(a, L=[]): L.append(a) return Lprint(func(1))print(func(2))print(func(3))
程序的输出是:
[1][1, 2][1, 2, 3]
这是因为默认值L在函数定义时生成,后面的调用(使用默认值,不给L传值)就会不断给定义时生成的默认list添加元素。
如果你希望使用这个诡异的特性也没问题,但要清楚它是什么。通常我们不建议默认值为可变对象,而是不可变的整数、浮点数、字符串等等。
关键字参数
调用函数时,我们可以使用“关键字参数”,它的形式是:kwarg=value
。比如前面的say_hi
函数的调用:
In [180]: say_hi(name='Tom')Hi Tom In [181]: say_hi(name='Tom', greeting='Hello')Hello Tom In [182]: say_hi(name='Tom', more='how are you')Hi Tom how are youIn [183]: say_hi(more='good day', name='Tom', greeting='Hi')Hi Tom good day
上面最后一个调用告诉我们,关键字参数是通过关键字来确认参数的,所以可以不用按照函数定义时的顺序传递参数。
关键字参数跟默认参数类似有参数位置的限制,关键字参数后面必须都是关键字参数。
下面的调用都是无效的:
say_hi() # 缺少必须的参数namesay_hi(name='Tom', 'Hello') # 关键字参数后面出现了非关键字参数say_hi('Tom', name='Tim') # 同样的参数传了两个值say_hi(age=10) # 函数定义中不存在的关键字参数
如果函数定义的最后一个参数是两个星号加名称:**name
,那么它接受一个字典包含所有关键字参数,这个字典不包括name前面声明的普通参数:
In [190]: def foo(a, **kwargs): ...: print(a) ...: for k, v in kwargs.items(): ...: print('%s : %s' % (k, v)) ...:In [191]: foo(1)1In [192]: foo(a=1)1In [193]: foo(a=1, b=2, c=3, d=4, e='a')1b : 2c : 3d : 4e : a
可见,**kwargs
把a后面的所有关键字参数都接收了。这对我们以后写参数非常多的函数时很有帮助。
可变参数列表
可变参数列表类似关键字参数**kwargs
,因为它没有key只有value,所以它是一个序列(确切的说是一个tuple),它的形式是*args
,名称前面有一个星号*
,用以接收不确定数量的参数。我们常用的内置函数print
就是一个可变参数函数。
下面我自己定义一个可变参数函数:
In [197]: def foo(*args): ...: print(type(args)) ...: print('|'.join(args)) ...:In [198]: foo('a', 'b', 'c', 'd')<class 'tuple'>a|b|c|d
同样的,可变参数后面必须跟关键字参数:
In [204]: def foo(*args, joiner='|'): ...: print(type(args)) ...: print(joiner.join(args)) ...:In [205]: foo('a', 'b', 'c', 'd')<class 'tuple'>a|b|c|dIn [206]: foo('a', 'b', 'c', 'd', joiner='/')<class 'tuple'>a/b/c/d
解包参数列表
这个正好跟可变参数列表相反,如果要调用的函数的参数值已经在list或tuple里面了,我们可以通过解包list或tuple来给函数传值。比如内置的range()
函数可以输入两个参数:start和stop,如果它们在一个llist或tuple里面,可以通过*
操作符解包来传值:
In [207]: list(range(1, 8))Out[207]: [1, 2, 3, 4, 5, 6, 7]In [208]: args = [1, 8]In [209]: list(range(*args))Out[209]: [1, 2, 3, 4, 5, 6, 7]
同样的,dict可以通过**
操作符传递关键字参数:
In [212]: d = {'name':'Tom', 'greeting':'Hello', 'more':'good day'}In [213]: say_hi(**d)Hello Tom good day
lambda函数
通过关键字lambda
来实现小的匿名函数。匿名函数返回一个函数对象,在可以作为函数参数传递给函数。比如内置函数sorted
中的key
参数就接受一个函数对象。
In [215]: func = lambda a, b: a * bIn [216]: func(1,2)Out[216]: 2In [217]: func(3,5)Out[217]: 15
再看看sorted使用lambda函数的情况,先对学生按姓名排序,再按年龄排序:
In [218]: students = [ ...: {'name': 'Tom', 'age': 12}, ...: {'name': 'Jack', 'age': 13}, ...: {'name': 'Aby', 'age': 10},]In [219]: sorted(students, key=lambda s: s['name'])Out[219]: [{'name': 'Aby', 'age': 10}, {'name': 'Jack', 'age': 13}, {'name': 'Tom', 'age': 12}]In [220]: sorted(students, key=lambda s: s['age'])Out[220]: [{'name': 'Aby', 'age': 10}, {'name': 'Tom', 'age': 12}, {'name': 'Jack', 'age': 13}]
变量的作用域和生命周期
程序中的变量不是在任何地方都可见的,它有自己的作用域。
局部变量
定义在函数内部的变量只在函数内部可见,也就是说,它是函数的局部变量。
In [1]: def func(): ...: x = 'hello' ...: print(x) ...:In [2]: func()helloIn [3]: x------------------------NameError Traceback (most recent call last)<ipython-input-3-6fcf9dfbd479> in <module>----> 1 xNameError: name 'x' is not defined
x是func
内部的一个变量,对该函数内部可见,所以print(x)
语句能打印x的值。但是在函数外部访问x时就会报错:x是未定义的。
全局变量
相对于局部变量,全局变量是定义在函数外部的,它具有全局作用域。
In [4]: x = 'hello'In [5]: def func2(): ...: print(x) ...:In [6]: func2()helloIn [7]: xOut[7]: 'hello'
如果要在函数内部修改全局变量,就需要用关键字global
来声明全局变量:
In [8]: def func3(): ...: global x ...: x += 'world' ...: print(x) ...:In [9]: func3()helloworldIn [10]: xOut[10]: 'helloworld'
局部变量变量的生命周期从函数调用开始,到函数运行结束为止;全局变量的生命周期直到整个程序结束为止。
删除函数
前面的章节中,我们使用关键字del
来删除列表或其中的元素,它同样可以用来删除函数:
In [11]: def func4(): ...: print('func4') ...:In [12]: func4()func4In [13]: del func4In [14]: func4()-------------------NameError Traceback (most recent call last)<ipython-input-14-0e6ad11a93c1> in <module>----> 1 func4()NameError: name 'func4' is not defined
在Python中,函数也是对象,所以用del
删除函数就跟删除其它对象一样。
文档字符串(docstring)
作为类、函数或模块之内的第一个表达式出现的字符串字面值。它在代码执行时会被忽略,但会被解释器识别并放入所在类、函数或模块的 doc 属性中。由于它可用于代码内省,因此是对象存放文档的规范位置。
In [15]: def my_func(): ...: '''first line is summary of this function ...: ...: the more lines are details of this function ...: ''' ...: pass ...:In [16]: print(my_func.__doc__)first line is summary of this function the more lines are details of this function
写docstring的规则一般是这样的:
(1)第一行简短概述该函数或类的功能
(2)第二行空白
(3)后面几行详细描述函数的参数、返回值等等
总结
定义函数时,参数称为“形参”,表述参数的类型;调用函数时,参数为“实参”,是传给函数的具体值。
定义函数时,可以为参数指定默认值;调用函数时,可以通过关键字参数调用。
定义函数是,可以指定参数为可变参数列表 *args
或**kwargs
;调用函数时,可以通过解包list,tuple和dict来传入参数。
转载: 感谢您对莫愁个人博客网站平台的认可,非常欢迎各位朋友分享到个人站长或者朋友圈,但转载请说明文章出处“来源莫愁个人博客 https://www.mochoublog.com/study/289.html”。
上一篇:Python 函数
下一篇:Python内置函数(一)