欢迎光临散文网 会员登陆 & 注册

简明Python教程·函数&模块

2023-02-12 08:00 作者:琉璃汐阳  | 我要投稿

上一篇专栏

 函数

函数(Functions)是指可重复使用的程序片段。它们允许你为某个代码块赋予名字,允许你 通过这一特殊的名字在你的程序任何地方来运行代码块,并可重复任何次数。这就是所谓的 调用(Calling)函数。我们已经使用过了许多内置的函数,例如 len range

函数概念可能是在任何复杂的软件(无论使用的是何种编程语言)中最重要的构建块,所以 我们接下来将在本章中探讨有关函数的各个方面。

函数可以通过关键字 def 来定义。这一关键字后跟一个函数的标识符名称,再跟一对圆括 号,其中可以包括一些变量的名称,再以冒号结尾,结束这一行。随后而来的语句块是函数 的一部分。下面的案例将会展示出这其实非常简单:

案例(保存为 functon1.py ):


输出:

它是如何工作的 

我们以上文解释过的方式定义名为 say_hello 的函数。这个函数不使用参数,因此在括号中 没有声明变量。函数的参数只是输入到函数之中,以便我可以传递不同的值给它,并获得相 应的结果。

要注意到我们可以两次调用相同的函数,这意味着我们不必重新把代码再写一次。 


 函数参数 

函数可以获取参数,这个参数的值由你所提供,借此,函数便可以利用这些值来做一些事 情。这些参数与变量类似,这些变量的值在我们调用函数时已被定义,且在函数运行时均已 赋值完成。

函数中的参数通过将其放置在用以定义函数的一对圆括号中指定,并通过逗号予以分隔。当 我们调用函数时,我们以同样的形式提供需要的值。要注意在此使用的术语——在定义函数 时给定的名称称作“形参”(Parameters),在调用函数时你所提供给函数的值称作“实 参”(Arguments)。

案例(保存为 function_param.py ):

输出:

它是如何工作的 

在这里,我们将函数命名为 print_max 并使用两个参数分别称作 a b 。我们使用一个 简单的 if...else 语句来找出更大的那个数,并将它打印出来。

第一次调用函数 print_max 时,我们以实参的形式直接向函数提供这一数字。在第二次调用 时,我们将变量作为实参来调用函数。 print_max(x, y) 将使得实参 x 的值将被赋值给形参 a ,而实参 y 的值将被赋值给形参 b 。在两次调用中, print_max 都以相同的方式工作。


 局部变量

函数 59当你在一个函数的定义中声明变量时,它们不会以任何方式与身处函数之外但具有相同名称 的变量产生关系,也就是说,这些变量名只存在于函数这一局部(Local)。这被称为变量的 作用域(Scope)。所有变量的作用域是它们被定义的块,从定义它们的名字的定义点开始。

案例(保存为 function_local.py ):

输出:

它是如何工作的

当我们第一次打印出存在于函数块的第一行的名为 x 的值时,Python 使用的是在函数声明之上的主代码块中声明的这一参数的值。

接着,我们将值 2 赋值给 x x 是我们这一函数的局部变量。因此,当我们改变函数中 x 的值的时候,主代码块中的 x 则不会受到影响。

 随着最后一句 print 语句,我们展示出主代码块中定义的 x 的值,由此确认它实际上不受 先前调用的函数中的局部变量的影响。


 global 语句 

如果你想给一个在程序顶层的变量赋值(也就是说它不存在于任何作用域中,无论是函数还 是类),那么你必须告诉 Python 这一变量并非局部的,而是全局(Global)的。我们需要通 过 global 语句来完成这件事。因为在不使用 global 语句的情况下,不可能为一个定义于 函数之外的变量赋值。

你可以使用定义于函数之外的变量的值(假设函数中没有具有相同名字的变量)。然而,这 种方式不会受到鼓励而且应该避免,因为它对于程序的读者来说是含糊不清的,无法弄清楚 变量的定义究竟在哪。而通过使用 global 语句便可清楚看出这一变量是在最外边的代码块 中定义的。 

案例(保存为 function_global.py ):

输出:

它是如何工作的 

global 语句用以声明 x 是一个全局变量——因此,当我们在函数中为 x 进行赋值时,这 一改动将影响到我们在主代码块中使用的 x 的值。

 你可以在同一句 global 语句中指定不止一个的全局变量,例如 global x, y, z


 默认参数值 

对于一些函数来说,你可能为希望使一些参数可选并使用默认的值,以避免用户不想为他们 提供值的情况。默认参数值可以有效帮助解决这一情况。你可以通过在函数定义时附加一个 赋值运算符( = )来为参数指定默认参数值。

要注意到,默认参数值应该是常数。更确切地说,默认参数值应该是不可变的——这将在后 面的章节中予以更详细的解释。就目前来说,只要记住就行了。

案例(保存为 function_default.py ):

输出:

它是如何工作的 

名为 say 的函数用以按照给定的次数打印一串字符串。如果我们没有提供一个数值,则将按 照默认设置,只打印一次字符串。我们通过为参数 times 指定默认参数值 1 来实现这一 点。

在第一次使用 say 时,我们只提供字符串因而函数只会将这个字符串打印一次。在第二次使 用 say 时,我们既提供了字符串,同时也提供了一个参数 5 ,声明我们希望说(Say)这 个字符串五次。

注意

只有那些位于参数列表末尾的参数才能被赋予默认参数值,意即在函数的参数列表中拥 有默认参数值的参数不能位于没有默认参数值的参数之前。 这是因为值是按参数所处的位置依次分配的。举例来说, def func(a, b=5) 是有效的, 但 def func(a=5, b) 是无效的。


 关键字参数

如果你有一些具有许多参数的函数,而你又希望只对其中的一些进行指定,那么你可以通过 命名它们来给这些参数赋值——这就是关键字参数(Keyword Arguments——我们使用命 名(关键字)而非位置(一直以来我们所使用的方式)来指定函数中的参数。

这样做有两大优点——其一,我们不再需要考虑参数的顺序,函数的使用将更加容易。其 二,我们可以只对那些我们希望赋予的参数以赋值,只要其它的参数都具有默认参数值。

案例(保存为 function_keyword.py ):

输出:

它是如何工作的

名为 func 的函数有一个没有默认参数值的参数,后跟两个各自带有默认参数值的参数。

在第一次调用函数时, func(3, 7) ,参数 a 获得了值 3 ,参数 b 获得了值 7 ,而 c 获得了默认参数值 10

 在第二次调用函数时, func(25, c=24) ,由于其所处的位置,变量 a 首先获得了值 25。然 后,由于命名——即关键字参数——指定,变量 c 获得了值 24 。变量 b 获得默认参数值 5

在第三次调用函数时, func(c=50, a=100) ,我们全部使用关键字参数来指定值。在这里要注 意到,尽管 a c 之前定义,但我们还是我们在变量 a 之前指定了变量 c


 可变参数 4

有时你可能想定义的函数里面能够有任意数量的变量,也就是参数数量是可变的,这可以通 过使用星号来实现(将下方案例保存为 function_varargs.py ):

输出:

它是如何工作的 

当我们声明一个诸如 *param 的星号参数时,从此处开始直到结束的所有位置参数 (Positional Arguments)都将被收集并汇集成一个称为“param”的元组(Tuple)。 

类似地,当我们声明一个诸如 **param 的双星号参数时,从此处开始直至结束的所有关键字 参数都将被收集并汇集成一个名为 param 的字典(Dictionary)。

 我们将在后面的章节探索有关元组与字典的更多内容。


 return 语句

return 语句用于从函数中返回,也就是中断函数。我们也可以选择在中断函数时从函数中返 回一个值。

 案例(保存为 function_return.py ): 

输出:

它是如何工作的 函数 64maximum 函数将会返回参数中的最大值,在本例中是提供给函数的数值。它使用一套简单的 if...else 语句来找到较大的那个值并将其返回。

要注意到如果 return 语句没有搭配任何一个值则代表着 返回 None None Python 中一 个特殊的类型,代表着虚无。举个例子, 它用于指示一个变量没有值,如果有值则它的值便 是 None(虚无)

每一个函数都在其末尾隐含了一句 return None ,除非你写了你自己的 return 语句。你可 以运行 print(some_function()) ,其中 some_function 函数不使用 return 语句,就像这样:

Python 中的 pass 语句用于指示一个没有内容的语句块。

提示:有一个名为 max 的内置函数已经实现了“找到最大数”这一功能,所以尽可能地使 用这一内置函数。


 DocStrings 

Python 有一个甚是优美的功能称作文档字符串(Documentation Strings),在称呼它时通常 会使用另一个短一些的名字docstringsDocStrings 是一款你应当使用的重要工具,它能够帮 助你更好地记录程序并让其更加易于理解。令人惊叹的是,当程序实际运行时,我们甚至可 以通过一个函数来获取文档!

案例(保存为 function_docstring.py ):

输出: 

它是如何工作的

函数的第一行逻辑行中的字符串是该函数的 文档字符串(DocString)。这里要注意文档字符 串也适用于后面相关章节将提到的模块(Modules)类(Class)

该文档字符串所约定的是一串多行字符串,其中第一行以某一大写字母开始,以句号结束。 第二行为空行,后跟的第三行开始是任何详细的解释说明。 在此强烈建议你在你所有重要功 能的所有文档字符串中都遵循这一约定。

我们可以通过使用函数的 __doc__ (注意其中的双下划綫)属性(属于函数的名称)来获取 函数 print_max 的文档字符串属性。只消记住 Python 将所有东西都视为一个对象,这其中 自然包括函数。我们将在后面的类(Class)章节讨论有关对象的更多细节。 

如果你曾使用过 Pythonhelp() 函数,那么你应该已经了解了文档字符串的用途了。它所 做的便是获取函数的 __doc__ 属性并以一种整洁的方式将其呈现给你。你可以在上方的函数 中尝试一下——只需在程序中包含 help(print_max) 就行了。要记住你可以通过按下 q 键 来退出 help

自动化工具可以以这种方式检索你的程序中的文档。因此,我强烈推荐你为你编写的所有重 要的函数配以文档字符串。你的 Python 发行版中附带的 pydoc 命令与 help() 使用文档字 符串的方式类似。


 总结

我们已经了解了许多方面的函数,但我们依旧还未覆盖到所有类型的函数。不过,我们已经 覆盖到了大部分你每天日常使用都会使用到的 Python 函数。 

接下来,我们将了解如何创建并使用 Python 模块。 .


  1. 原文作 Function Parameters,沈洁元译本译作“函数形参”。Parameter 和 Argument 同时具有“参数”和“形参”或“实参”两种译法。一般来说,只有在存在形参实参二义关系 时,才会特别翻译成“形参”或“实参”。故此节标题 Parameter 作“参数”解。 ↩

  2. 原文作 Local Varibles。 ↩ 

  3. 原文作 Keyword Arguments,沈洁元译本译作“关键参数”。 ↩ 

  4. 原文作 VarArgs Parameters,VarArgs 来自于英文“可变的”“自变量(一译变元,台译 引数,也可以理解成参数)”两个英文单词的结合,即 Variable Arguments。 ↩

  5. 此处指的是以英文撰写的文档字符串内容。 ↩

 模块

在上一章,你已经了解了如何在你的程序中通过定义一次函数工作来重用代码。那么如果你 想在你所编写的别的程序中重用一些函数的话,应该怎么办?正如你可能想象到的那样,答 案是模块(Modules)。 

编写模块有很多种方法,其中最简单的一种便是创建一个包含函数与变量、以 .py 为后缀的 文件。 

另一种方法是使用撰写 Python 解释器本身的本地语言来编写模块。举例来说,你可以使用 C 语言来撰写 Python 模块,并且在编译后,你可以通过标准 Python 解释器在你的 Python 代 码中使用它们。 

一个模块可以被其它程序导入并运用其功能。我们在使用 Python 标准库的功能时也同样如 此。首先,我们要了解如何使用标准库模块。 

案例 (保存为 module_using_sys.py ):

输出:

它是如何工作的

首先,我们通过 import 语句导入 sys 模块。基本上,这句代码将转化为我们告诉 Python 我们希望使用这一模块。 sys 模块包含了与 Python 解释器及其环境相关的功能,也就是所 谓的系统功能(system)。

Python 运行 import sys 这一语句时,它会开始寻找 sys 模块。在这一案例中,由于其 是一个内置模块,因此 Python 知道应该在哪里找到它。

如果它不是一个已编译好的模块,即用 Python 编写的模块,那么 Python 解释器将从它的 sys.path 变量所提供的目录中进行搜索。如果找到了对应模块,则该模块中的语句将在开始 运行,并能够为你所使用。在这里需要注意的是,初始化工作只需在我们第一次导入模块时 完成。

sys 模块中的 argv 变量通过使用点号予以指明,也就是 sys.argv 这样的形式。它清晰地 表明了这一名称是 sys 模块的一部分。这一处理方式的另一个优点是这个名称不会与你程序 中的其它任何一个 argv 变量冲突。

sys.argv 变量是一系列字符串的列表(List)(列表将在后面的章节予以详细解释)。具体 而言, sys.argv 包含了命令行参数(Command Line Arguments)这一列表,也就是使用命 令行传递给你的程序的参数。

 如果你正在使用一款 IDE 来编写并运行这些程序,请在程序菜单中寻找相关指定命令行参数 的选项。

 在这里,当我们运行 python module_using_sys.py we are arguments 时,我们通过 python 命 令来运行 module_using_sys.py 模块,后面的内容则是传递给程序的参数。 Python 将命令行 参数存储在 sys.argv 变量中供我们使用。

 在这里要记住的是,运行的脚本名称在 sys.argv 的列表中总会位列第一。因此,在这一案 例中我们将会有如下对应关系: 'module_using_sys.py' 对应 sys.argv[0] 'we' 对应 sys.argv[1] 'are' 对应 sys.argv[2] 'arguments' 对应 sys.argv[3] 。要注意到 Python0 开始计数,而不是 1

  sys.path 内包含了导入模块的字典名称列表。你能观察到 sys.path 的第一段字符串是空的 ——这一空字符串代表当前目录也是 sys.path 的一部分,它与 PYTHONPATH 环境变量等 同。这意味着你可以直接导入位于当前目录的模块。否则,你必须将你的模块放置在 sys.path 内所列出的目录中。

另外要注意的是当前目录指的是程序启动的目录。你可以通过运行 import os; print(os.getcwd()) 来查看你的程序目前所处在的目录。


 按字节码编译的 .pyc 文件

导入一个模块是一件代价高昂的事情,因此 Python 引入了一些技巧使其能够更快速的完成。 其中一种方式便是创建按字节码编译的(Byte-Compiled)文件,这一文件以 .pyc 为其扩展 名,是将 Python 转换成中间形式的文件(还记得《介绍》一章中介绍的 Python 是如何工作 的吗?)。这一 .pyc 文件在你下一次从其它不同的程序导入模块时非常有用——它将更加 快速,因为导入模块时所需要的一部分处理工作已经完成了。同时,这些按字节码编译的文 件是独立于运行平台的。

注意:这些 .pyc 文件通常会创建在与对应的 .py 文件所处的目录中。如果 Python 没有相 应的权限对这一目录进行写入文件的操作,那么 .pyc 文件将不会被创建。


 from..import 语句

如果你希望直接将 argv 变量导入你的程序(为了避免每次都要输入 sys. ),那么你可以 通过使用 from sys import argv 语句来实现这一点。

警告:一般来说,你应该尽量避免使用 from...import 语句,而去使用 import 语句。 这是为了避免在你的程序中出现名称冲突,同时也为了使程序更加易读。 

案例:


 模块的 __name__ 

每个模块都有一个名称,而模块中的语句可以找到它们所处的模块的名称。这对于确定模块 是独立运行的还是被导入进来运行的这一特定目的来说大为有用。正如先前所提到的,当模 块第一次被导入时,它所包含的代码将被执行。我们可以通过这一特性来使模块以不同的方 式运行,这取决于它是为自己所用还是从其它从的模块中导入而来。这可以通过使用模块的 __name__ 属性来实现。 

案例(保存为 module_using_name.py ):

输出:

它是如何工作的 

每一个 Python 模块都定义了它的 __name__ 属性。如果它与 __main__ 属性相同则代表这一 模块是由用户独立运行的,因此我们便可以采取适当的行动。


 编写你自己的模块 

编写你自己的模块很简单,这其实就是你一直在做的事情!这是因为每一个 Python 程序同时 也是一个模块。你只需要保证它以 .py 为扩展名即可。下面的案例会作出清晰的解释。 

案例(保存为 mymodule.py ):

上方所呈现的就是一个简单的模块。正如你所看见的,与我们一般所使用的 Python 的程序相 比其实并没有什么特殊的区别。我们接下来将看到如何在其它 Python 程序中使用这一模块。

要记住该模块应该放置于与其它我们即将导入这一模块的程序相同的目录下,或者是放置在 sys.path 所列出的其中一个目录下。

另一个模块(保存为 mymodule_demo.py ):

输出:

它是如何工作的 

你会注意到我们使用相同的点符来访问模块中的成员。Python 很好地重用了其中的符号,这 充满了“Pythonic”式的气息,这使得我们可以不必学习新的方式来完成同样的事情。 

下面是一个使用 from...import 语法的范本(保存为 mymodule_demo2.py ):

mymodule_demo2.py 所输出的内容与 mymodule_demo.py 所输出的内容是一样的。

在这里需要注意的是,如果导入到 mymodule 中的模块里已经存在了 __version__ 这一名 称,那将产生冲突。这可能是因为每个模块通常都会使用这一名称来声明它们各自的版本号。因此,我们大都推荐最好去使用 import 语句,尽管这会使你的程序变得稍微长一些。

你还可以使用:

这将导入诸如 say_hi 等所有公共名称,但不会导入 __version__ 名称,因为后者以双下划 线开头。

警告:要记住你应该避免使用 import-star 这种形式,即 from mymodule import *

Python 之禅 

Python 的一大指导原则是“明了胜过晦涩” 。你可以通过在 Python 中运行 import this 来了解更多内容。


 dir 函数

 内置的 dir() 函数能够返回由对象所定义的名称列表。 如果这一对象是一个模块,则该列 表会包括函数内所定义的函数、类与变量。

 该函数接受参数。 如果参数是模块名称,函数将返回这一指定模块的名称列表。 如果没有提 供参数,函数将返回当前模块的名称列表。 

案例:

它是如何工作的 

首先我们看到的是 dir 在被导入的 sys 模块上的用法。我们能够看见它所包含的一个巨大 的属性列表。 

随后,我们以不传递参数的形式使用 dir 函数。在默认情况下,它将返回当前模块的属性列 表。要注意到被导入进来的模块所能生成的列表也会是这一列表的一部分。 

给了观察 dir 函数的操作,我们定义了一个新的变量 a 并为其赋予了一个值,然后在检查 dir 返回的结果,我们就能发现,同名列表中出现了一个新的值。我们通过 del 语句移除 了一个变量或是属性,这一变化再次反映在 dir 函数所处的内容中。

关于 del 的一个小小提示——这一语句用于删除一个变量或名称,当这一语句运行后,在本 例中即 del a ,你便不再能访问变量 a ——它将如同从未存在过一般。

要注意到 dir() 函数能对任何对象工作。例如运行 dir(str) 可以访问 str String,字符 串)类的属性。

 同时,还有一个 vars() 函数也可以返回给你这些值的属性,但只是可能,它并不能针对所 有类都能正常工作。


现在,你必须开始遵守用以组织你的程序的层次结构。变量通常位于函数内部,函数与全局 变量通常位于模块内部。如果你希望组织起这些模块的话,应该怎么办?这便是包 (Packages)应当登场的时刻。

包是指一个包含模块与一个特殊的 __init__.py 文件的文件夹,后者向 Python 表明这一文 件夹是特别的,因为其包含了 Python 模块。

 建设你想创建一个名为“world”的包,其中还包含着 ”asia“、”africa“等其它子包,同时这些子 包都包含了诸如”india“、”madagascar“等模块。

 下面是你会构建出的文件夹的结构:

包是一种能够方便地分层组织模块的方式。你将在 标准库 中看到许多有关于此的实例。


 总结

如同函数是程序中的可重用部分那般,模块是一种可重用的程序。包是用以组织模块的另一 种层次结构。Python 所附带的标准库就是这样一组有关包与模块的例子。

我们已经了解了如何使用这些模块并创建你自己的模块。

接下来,我们将学习一些有趣的概念,它们被称作数据结构。 .

  1. 原文作 Byte-compiled .pyc Files,沈洁元译本译作“字节编译的 .pyc 文件”。 ↩ 

  2. 原文作 Explicit is better than Implicit,如果使用前面章节出现过的术语概念,也可理 解为“显式胜过隐式”。 ↩

下一篇专栏


简明Python教程·函数&模块的评论 (共 条)

分享到微博请遵守国家法律