简介

函数是程序的可复用片段,允许你为语句块赋予名字之后在程序的任何地方运行它们任意次,这称做函数调用。

我们已经使用过一些内建函数,例如len和range等。

函数也许是任何有意义的软件中最重要的构件,所以我们将在本章探究函数的方方面面。

函数以关键字def定义,其后紧跟函数名,由一对小括号闭合的形参,最后以冒号结束定义行,

定义行下面的是函数体,它是一个语句块。

听着有点复杂,其实定义起来是很简单的,见下面的例子:

范例

#!/usr/bin/python

# Filename: function1.py

def sayHello():

print('Hello World!') # 语句块也就是函数体

# 结束函数定义

sayHello() # 调用函数

sayHello() # 再次调用

输出:

$ python function1.py

Hello World!

Hello World!

工作流程

我用使用上面讲解的函数定义语法定义了一个名叫sayHello的函数,这个函数没有形参因此小括号里也就没有

定义变量。函数形参只是提供给函数的输入,所以我们可以为函数传递不同的形参而后得到相应的结果。

注意我们调用了相同的函数两次,这也意味着我们无需多次编写相同的代码(注:函数体)。

函数形参

一个函数可以拥有形参,它们是你提供给函数的值,因此函数就可以利用这些值进行一些操作了。

函数形参非常类似变量,只是这些变量是在我们调用函数时定义的并在函数运行前被赋值。

形参在函数定义中的小括号内指定,并以逗号分隔。当我们调用函数时以同样的方式提供形参值。

注意下面两个术语的区别 – 函数定义中给定的参数叫做形参,而在函数调用提供的值叫做 – 实参

范例:

#!/usr/bin/python

# Filename: func_param.py

def printMax(a, b):

if a > b:

print(a, 'is maximum')

elif a == b:

print(a, 'is equal to', b)

else:

print(b, 'is maximum')

printMax(3, 4) # 直接提供字面值

x = 5

y = 7

printMax(x, y) # 以变量作为实参

输出:

$ python func_param.py

4 is maximum

7 is maximum

工作流程:

我们定义的函数叫做printMax,它需要两个形参,分别叫做ab

函数利用简单的if…else…语句找出两者中较大的数并将其打印。

第一次调用printMax时我们直接使用数字作为实参,而第二次调用时我们使用变量。

printMax(x, y)促使实参x的值赋给形参a, y赋给b

两次调用中,pringMax的工作方式完全相同。

局部变量

在函数内声明的变量与在函数外的同名变量没有任何关系,即变量名对于函数是局部的。

这被称作变量的作用域,变量的作用域开始于它们所在块中定义它们的定义点处。

范例:

#!/usr/bin/python

# Filename: func_local.py

x = 50

def func(x):

print('x is', x)

x = 2

print('Changed local x to', x)

func(x)

print('x is still', x)

输出:

$ python func_local.py

x is 50

Changed local x to 2

x is still 50

如何工作:

在函数内部,我们首先使用变量x的值时,python引用的是函数形参x的值。

接下来,我们为x赋值2,因为x是这个函数的局部名字,所以当我们在函数中使用x的时候,

不会影响到主块中的x ,在最后的print调用中我们打印了主块中x的值也证明了这点。

使用global语句

当你希望为程序的顶级名字赋值时(没有定义在任何其他作用域中的变量,比如函数或类作用域),

(注: python有名字空间的概念,名字空间建立起名字与实际对象(/数据)的映射关系,

这里的”名字”指的是名字所对应的对象)。这时你必须告诉python,名字不是局部而是全局的。

通过global语句可以做到这点,否则是不可能对一个定义在函数外的变量赋值的。

你可以使用定义在函数外的变量的值(但要假设函数内没有同名变量),不过并不鼓励这种用法而是应该

尽量避免,这会让代码的读者难以弄清变量到底定义在哪?

范例:

#!/usr/bin/python

# Filename: func_global.py

x = 50

def func():

    global x

    print('x is', x)

    x = 2

    print('Changed global x to', x)

func()

print('Value of x is', x)

输出:

    $ python func_global.py

    x is 50

    Changed global x to 2

    Value of x is 2

工作流程:

global语句用于声明x是一个全局变量 – 因此我们在函数内为x赋值后变化也会反映在主块中对x的使用中。

你也可以使用一个global语句指定多个全局变量,比如global x, y, z

使用nonlocal语句

我们已经看到如何存取局部和全局变量。还有另一类叫做”非局部”的作用域,它介于局部和全局之间。

当在函数内定义函数时你会注意到非局部作用域。

因为python中的一切只是可执行代码,所以你可以在任何地方定义函数。让我们看一个例子:

#!/usr/bin/python

# Filename: func_nonlocal.py

def func_outer():

    x = 2

    print('x is', x)

    def func_inner():

        nonlocal x

        x = 5

    func_inner()

    print('Changed local x to', x)

func_outer()

输出:

    $ python func_nonlocal.py

    x is 2

    Changed local x to 5

工作流程:

当我们处于func_inner函数中时,func_outer第一行定义的变量x既不是局部也不是全局变量。

这时通过nonlocal x我们声明这个x是非局部的,所以我们才能够存取它。

试着将nonlocal x改为global x观察这两种用法有什么不同。

默认实参值

对于一些函数,你可能希望它们的形参是可选的,并当用户没有为这些形参提供值的时候给它们一个默认值。

这需要借助默认实参值。默认实参值在函数定义时通过为形参名赋一个默认值实现。

注意默认实参值应该是一个常量,更确切的应该是一个不可变类型 – 后面的章节会有具体解释,现在只要记住这

点就可以了。

范例:

#!/usr/bin/python

# Filename: func_default.py

def say(message, times = 1):

    print(message * times)

say('Hello')

say('World', 5)

输出:

    $ python func_default.py

    Hello

    WorldWorldWorldWorldWorld

工作流程:

函数say用于以指定次数打印指定字符串. 如果我们没有指定次数则默认的字符串只被打印1次。

我们通过为形参times指定一个默认实参值1实现这个功能。

第一次调用say时,我们只提供了被打印的字符串然后函数只打印一次。

而第二次调用时,我们不仅提供了被打印的字符串还提供了实参5以表明我们需要字符串被打印5次。

重点:

只有在实参列表靠后的参数才能拥有默认实参值,即你不能先声明带有默认实参值的形参再声明不带有默认实参值的参数。

这是因为实参值是根据形参的位置赋给形参的,例如:def func(a, b = 5)合法,但def func(a = 5, b)就非法了。

关键实参

如果你有一些函数拥有许多参数,但你只想使用其中的几个,这时你可以通过形参名为其赋值。

这被称做关键实参- 使用形参名(关键字)为函数指定实参而不是我们一直使用的通过位置指定实参。

这样做有两个优点,首先函数用起来更简单,因为我们不用操心实参的顺序了。

其次,可以只为我们感兴趣的形参赋值,如果其它参数带有默认实参值的话。

范例:

#!/usr/bin/python

# Filename: func_key.py

def func(a, b=5, c=10):

    print('a is', a, 'and b is', b, 'and c is', c)

func(3, 7)

func(25, c=24)

func(c=50, a=100)

输出:

    $ python func_key.py

    a is 3 and b is 7 and c is 10

    a is 25 and b is 5 and c is 24

    a is 100 and b is 5 and c is 50

工作流程:

函数func拥有一个不带有默认实参值的参数,后跟两个带有默认实参值的参数。

第一次调用func(3, 7),形参a得到值3,b7c为默认值10

第二次调用func(25, c=24), 因为a在参数表中所处的位置,它得到25

而c通过引用其名字得到24也就是关键字实参的功能。b为默认值5

第三次调用func(c=50, a=100), 我们只使用关键字实参指定形参值。

注意尽管在函数定义中a比c更早定义,但我们仍然可以先指定c再指定a。

可变参数(VarArgs)

TODO

因为我们还没有讨论列表和字典,我是不是应该将这部分放到后面的章节?

有时你可能希望编写一个可以接受任意多个形参的函数,使用星号可以帮你做到:

#!/usr/bin/python

# Filename: total.py

def total(initial=5, *numbers, **keywords):

    count = initial

    for number in numbers:

        count += number

    for key in keywords:

        count += keywords[key]

    return count

print(total(10, 1, 2, 3, vegetables=50, fruits=100))

输出:

    $ python total.py

    166

工作流程:

当我们以星号声明一个形参比如*param,那么这个参数点之后的所有实参会被收集成一个列表,

本例中这个列表叫做param。与之类似如果我们以双星号声明一个形参,它会被收集成一个关键字实参字典。

后面的章节我们会研究列表和字典。

只能以关键字赋值的形参(Keyword-only Parameters)

(注:为方便理解和翻译,以后直接使用英文术语 keyword-only)

如果我们希望某些关键字形参只能通过关键字实参得到而不是按照实参的位置得到,可以将其声明在星号形参后面:

#!/usr/bin/python

# Filename: keyword_only.py

def total(initial=5, *numbers, vegetables):

    count = initial

    for number in numbers:

        count += number

    count += vegetables

    return count

print(total(10, 1, 2, 3, vegetables=50))

print(total(10, 1, 2, 3))

# 引发错误,因为我们没有为vegetables提供默认实参值

输出:

    $ python keyword_only.py

    66

    Traceback (most recent call last):

      File "test.py", line 12, in <module>

    print(total(10, 1, 2, 3))

    TypeError: total() needs keyword-only argument vegetables

工作流程:

在星号形参后面声明的形参导致它成为keyword-only实参。

如果没有为这些实参提供一个默认值,那么必须在调用函数时以关键字实参为其赋值,否则将引发错误。

如果你只需要keyword-only实参但不需要星号实参,那么可以简单的省略星号实参的实参名。

例如def total(initial=5, *, vegetables)

return语句

return用于从函数返回,即跳出函数。也可以利用return语句从函数返回一个值。

范例:

#!/usr/bin/python

# Filename: func_return.py

def maximum(x, y):

    if x > y:

        return x

    else:

        return y

print(maximum(2, 3))

输出:

    $ python func_return.py

    3

工作流程:

maximum函数用于返回参数中的最大值,本例中我们以数字作为参数调用它。

函数使用一个简单的if…else语句比较出最大值并使用return语句将其返回。

注意一个不带有返回值的return语句相当于返回return None

None是python的一个特殊类型,代表空。例如如果一个变量的值为None则代表它不存在值。

每个函数的末尾都隐含的包含一个return None语句除非你编写了自己的return语句。

你可以通过print(someFunction())证实这点,someFunction是一个没有显式使用return语句的函数。例如:

def someFunction():

       pass

其中pass语句用来指示一个空语句块。

提示

python已经包含了一个被称作max的内建函数,它的功能即是寻找最大值,所以尽可能的使用这个函数吧。

DocStrings

python拥有一个俏皮的特性被称作文档字符串,通常它被简称为docstrings。

文档字符串是一个你应该利用的重要的工具,因为它帮助你更好的注释程序使得程序更易于理解。

更神奇的是你甚至可以在程序运行时取得文档字符串!

范例:

#!/usr/bin/python

# Filename: func_doc.py

def printMax(x, y):

    '''Prints the maximum of two numbers.

    The two values must be integers.'''

    x = int(x) # convert to integers, if possible

    y = int(y)

    if x > y:

        print(x, 'is maximum')

    else:

        print(y, 'is maximum')

printMax(3, 5)

print(printMax.__doc__)

输出:

    $ python func_doc.py

    5 is maximum

    Prints the maximum of two numbers.

   

            The two values must be integers.

工作流程:

一个函数的第一个逻辑行的字符串将成为这个函数的文档字符串。

注意类和模块同样拥有文档字符串,在后面相应的章节我们会学到它们。

根据惯例,文档字符串是一个多行字符串,其中第一行以大写字母开头,并以句号结尾。

接下来的第二行为空行,从第三行开始为详细的描述。

我强烈建议你在你的正规函数中遵循这个编写文档字符串的惯例。

我们可以通过使用函数的__doc__属性(注意双下划线)存取printMax的文档字符串。

记住python中的一切都是对象,其中也包括函数。在后面的类一章我们会学到更多。

如果你在python使用过help(),其实你已经看到过文档字符串的应用了!

help()只是取出函数的__doc__属性,然后以一种整洁的方式显示给你。

你可以用上面的函数作个实验 – 在你的程序中包含help(printMax)即可,记住按q键退出help。

函数注解(Annotations)

函数还拥有另一个被称作函数注解的高级特性,对于附加额外的形参和返回值信息非常有用。

因为python语言本身并不提供这样的功能(甩给了第三方库,具体实现方式第三方库说了算)。

所以在我们的讨论中决定跳过这一特性。如果你对函数注解有兴趣可以参见python增强提议No.3107

(http://www.python.org/dev/peps/pep-3107/)(注:python3已经支持这个特性了)

小结

我们已经看到了函数的方方面面,但注意这些不是函数的所有方面。

不过现有的关于python函数的知识已经足够应付日常应用了。

接下来我们将学习如何使用和创建pthon模块。

简明Python3教程 9.函数的更多相关文章

  1. 简明Python3教程(A Byte of Python 3)

    关键字:[A Byte of Python v1.92(for Python 3.0)] [A Byte of Python3] 简明Python教程 Python教程 简明Python3教程  简明 ...

  2. 简明Python3教程 16.标准库

    简介 python标准库作为python标准安装的一部分,其自身包含数量庞大的实用模块, 因此熟悉python标准库非常重要,因为很多问题都能利用python标准库快速解决. 下面我们将研究标准库中的 ...

  3. 简明Python3教程 6.基础

    你肯定不满足于只打印"Hello World"吧? 你想要的更多 - 你希望得到一些输入,操纵它后再从中得到某些东西.我们可以使用python中的常量和变量实现这些功能. 字面常量 ...

  4. 简明Python3教程 5.第一步

    介绍 我们现在来看看如何在Python中运行传统的”Hello world”程序.这会教你如何写.保存以及运行Python程序. 有两种办法来运行您的Python程序——使用交互式的解释器提示符或者源 ...

  5. 简明python教程三-----函数

    函数通过def关键字定义.def关键字后跟一个函数的表标识符名称,然后跟一对圆括号. 圆括号之中可以包括一些变量名,该行以冒号结尾.接下来是一块语句,它们是函数体. def sayHello(): p ...

  6. 简明Python3教程 1.介绍

    Python是少有的几种既强大又简单的编程语言.你将惊喜地发现通过使用Python即可轻松专注于解决问题而非和你所用的语言格式与结构. 下面是Python的官方介绍: Python is an eas ...

  7. 简明Python3教程 18.下一步是什么

    如果你有认真通读本书之前的内容并且实践其中包含的大量例程,那么你现在一定可以熟练使用python了. 同时你可能也编写了一些程序用于验证python特性并提高你的python技能.如果还没有这样做的话 ...

  8. 简明Python3教程 17.更多

    简介 迄今为止我们已经学习了python中的大多数常用知识.本章中我们会接触到更多的知识,使得我们更全面的掌握python. 传递元组 你是否希望过从函数返回两个不同的值?做到这点使用元组即可. &g ...

  9. 简明Python3教程 15.异常

    简介 当程序发生意外情况时则产生异常. 例如你需要读一个文件而这个文件并不存在会咋样?又或者是程序运行时你把它误删除了呢? 上述情形通过异常进行处理. 类似的,如果你的程序存在一些非法语句会发生什么呢 ...

随机推荐

  1. [原]MFC中DIALOG(对话框)程序响应加速键(快捷键)

    [原]MFC中DIALOG(对话框)程序响应加速键(快捷键) 2014-8-6阅读266 评论0 新建一个对话框程序,项目名为Test,删除默认确定,取消和静态文本框控件.添加一个按钮,Caption ...

  2. 【例题3-3 UVA - 401】Palindromes

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 如果一个字符没有对应的镜像,那么它对应的是一个空格. 然后注意 aba这种情况. 这种情况下b也要查一下它的镜像是不是和b一样. [ ...

  3. [Angular2 Form] Model Driven Form Custom Validator

    In this tutorial we are going to learn how simple it is to create custom form field driven validator ...

  4. 一起学libcef--给你的浏览器删除cookie

    long long ago, 我们讨论了如给你cef设置cookie. 如今来补充一点,假设给你的浏览器删除某一cookie. review一下设置cookie: std::wstring usern ...

  5. 12行Python暴力爬《黑豹》豆瓣短评

    作者:黄嘉锋 来源:https://www.jianshu.com/p/ea0b56e3bd86 草长莺飞,转眼间又到了三月"爬虫月".这时往往不少童鞋写论文苦于数据获取艰难,辗转 ...

  6. 【u118】日志分析

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] M 海运公司最近要对旗下仓库的货物进出情况进行统计.目前他们所拥有的唯一记录就是一个记录集装箱进出情况 ...

  7. 【MySQL】15个有用的MySQL/MariaDB性能调整和优化技巧

    MySQL 是一个强大的开源关系数据库管理系统(简称 RDBMS).它发布于 1995 年(20年前).它采用结构化查询语言(SQL),这可能是数据库内容管理中最流行的选择.最新的 MySQL 版本是 ...

  8. javascript中定义事件的三种方式 分类: C1_HTML/JS/JQUERY 2014-08-07 10:27 634人阅读 评论(0) 收藏

    在javascript中,可以为某个元素指定事件,指定的方式有以下三种: 1.在html中,使用onclick属性 2.在javascript中,使用onclick属性 3.在javascipt中,使 ...

  9. [内核编程] 4.1 技术原理 & 4.2 键盘过滤框架

    4.1 技术原理 & 4.2 键盘过滤框架 4.1 预备知识 符号链接:符号链接其实就是一个“别名”.可以用一个不同的名字来代表一个设备对象(实际上),符号链接可以指向任何有名字的对象. Zw ...

  10. PHP+Aax实现异步验证

    利用Ajax技术来检测用户名是否存在的原理流程图: 最终结果截图: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional/ ...