资讯专栏INFORMATION COLUMN

python基础教程:函数(2)

everfight / 2334人阅读

摘要:我们常用的内置函数就是一个可变参数函数。匿名函数返回一个函数对象,在可以作为函数参数传递给函数。定义函数时,可以为参数指定默认值调用函数时,可以通过关键字参数调用。

上一节我们学习了函数的定义和调用,理解了基本的函数知识。本节进一步学习函数相关的更多内容,深入了解函数,包括:默认参数、关键字参数、位置参数、变量的作用域等等。

形参和实参的不同

首先,我们先搞清两个概念:

形参(parameters),是定义函数时声明的参数名称,它定义了函数可以接受的参数类型;
实参(arguments),是调用函数时传给函数的实际值。
比如下面的函数定义:

def func(foo, bar=True, **kwargs):
    pass

foo, bar, kwargs 是函数的形参(parameters)。

当我们调用func时,比如:

func(20, bar=False, abc="xyz")

传给函数的值20False"xyz"就是实参。

默认参数值

默认参数就是在函数定义时,给参数一个默认值。调用该函数时可以不给有默认值的参数传值,这样调用时的参数可以减少。

看下面的例子:

def say_hi(name, greeting="Hi", more=""):
    print(greeting, name, more)

这个函数有三个参数,其中greetingmore有默认值,调用该函数时,有以下几种形式:

只传一个值给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 "", 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 L

print(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 you

In [183]: say_hi(more="good day", name="Tom", greeting="Hi")
Hi Tom good day

上面最后一个调用告诉我们,关键字参数是通过关键字来确认参数的,所以可以不用按照函数定义时的顺序传递参数。

关键字参数跟默认参数类似有参数位置的限制,关键字参数后面必须都是关键字参数。
下面的调用都是无效的:

say_hi()  # 缺少必须的参数name
say_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)
1

In [192]: foo(a=1)
1

In [193]: foo(a=1, b=2, c=3, d=4, e="a")
1
b : 2
c : 3
d : 4
e : 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")

a|b|c|d

同样的,可变参数后面必须跟关键字参数:

In [204]: def foo(*args, joiner="|"): 
     ...:     print(type(args)) 
     ...:     print(joiner.join(args)) 
     ...:

In [205]: foo("a", "b", "c", "d")

a|b|c|d

In [206]: foo("a", "b", "c", "d", joiner="/")

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 * b

In [216]: func(1,2)
Out[216]: 2

In [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()
hello

In [3]: x
------------------------
NameError        Traceback (most recent call last)
 in 
----> 1 x

NameError: 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()
hello

In [7]: x
Out[7]: "hello"

如果要在函数内部修改全局变量,就需要用关键字global来声明全局变量:

In [8]: def func3(): 
   ...:     global x 
   ...:     x += "world" 
   ...:     print(x) 
   ...:

In [9]: func3()
helloworld

In [10]: x
Out[10]: "helloworld"

局部变量变量的生命周期从函数调用开始,到函数运行结束为止;全局变量的生命周期直到整个程序结束为止。

删除函数

前面的章节中,我们使用关键字del来删除列表或其中的元素,它同样可以用来删除函数:

In [11]: def func4(): 
    ...:     print("func4") 
    ...:

In [12]: func4()
func4

In [13]: del func4

In [14]: func4()
-------------------
NameError          Traceback (most recent call last)
 in 
----> 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.ucloud.cn/yun/43788.html

相关文章

  • Python爬虫学习路线

    摘要:以下这些项目,你拿来学习学习练练手。当你每个步骤都能做到很优秀的时候,你应该考虑如何组合这四个步骤,使你的爬虫达到效率最高,也就是所谓的爬虫策略问题,爬虫策略学习不是一朝一夕的事情,建议多看看一些比较优秀的爬虫的设计方案,比如说。 (一)如何学习Python 学习Python大致可以分为以下几个阶段: 1.刚上手的时候肯定是先过一遍Python最基本的知识,比如说:变量、数据结构、语法...

    liaoyg8023 评论0 收藏0
  • python基础教程函数函数函数,重要的事说三遍

    摘要:调用函数就是通过函数名再传入它需要的参数即可。递归函数递归函数就是函数自己调用自己。我们以计算一个整数的阶乘来看看递归函数是什么样子的。阶乘的公式如下它的递归函数的定义如下递归函数都有一个跳出递归的条件,在本函数中就是。 函数,是编程中很重要的一个概念。简单来说,函数是一段可重复使用的代码段,给这段代码起个名字就是函数名。在程序的任何地方都可以通过函数名来使用这段代码,这就是函数调用。...

    dreamtecher 评论0 收藏0
  • python基础教程:输入和输出(IO)

    摘要:计算机编程中,会经常涉及输入输出,输入输出。提供功能的设备就是输入输出设备,比如,键盘,鼠标就是典型的输入设备,显示器就是典型的输出设备。的语言提供了方便的输入输入和输出印刷函数。 计算机编程中,会经常涉及输入输出(IO,输入/输出)。提供IO功能的设备就是输入输出设备,比如,键盘,鼠标就是典型的输入设备,显示器就是典型的输出设备。程序在运行过程中,数据存储在内存中,但有时候它需要用户...

    zzir 评论0 收藏0
  • 编程零基础应当如何开始学习 Python

    摘要:首先,在学习之前一定会考虑一个问题版本选择对于编程零基础的人来说,选择。建议从下面课程开始教程标准库官方文档非常贴心地提供中文翻译首先需要学习的基础知识,下载安装导入库字符串处理函数使用等等。 提前说一下,这篇福利多多,别的不说,直接让你玩回最有手感的怀旧游戏,参数贴图很方便自己可以根据喜好修改哦。 本篇通过以下四块展开,提供大量资源对应。 showImg(https://segmen...

    JackJiang 评论0 收藏0
  • Python基础教程第二版》第二章-Python列表和元祖

    摘要:第二个索引元素不在分片内。显示操作索引指向第个元素,第个元素不存在。序列加法乘法表示个空列表空列表用初始化成员资格运算符布尔运算符,检查值是否在序列中。修改列表,没有返回值。在原位置对列表排序。 序列 例: Edward = [abc, 12] 序列中的所有元素都是有编号的,从0开始递增。 Note: 使用负数索引-1,Python会从右边,也就是从最后一个元素开始计数。最后一个元素...

    darryrzhong 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<