• [阅读]Think Python CH3 Functions


    调用函数

    在程序中,函数是一段被命名的语句,进行某个运算。当你定义一个函数时,需要指明函数名和语句。然后,可以通过函数名调用函数。我们已经见过函数调用了:

    >>> type(32)
    <type 'int'>

    函数名为type。括号内的表达式叫做函数的参数。这个函数的结果,是返回参数的类型。

    一般来说,函数都需要获得参数,并返回结果。结果被叫做返回值

    类型转换函数

    Python提供内建函数,将一种类型的值转化为另外一种。int函数将任何可以转化的参数值转化为一个整数,否则提示错误:

    >>> int('32')
    32
    >>> int('Hello')
    ValueError: invalid literal for int(): Hello

    int可以把浮点数转化为整数,但是不会四舍五入,而是截断小数部分:

    >>> int(3.99999)
    3
    >>> int(-2.3)
    -2

    float将整数或者字符串转化成浮点数:

    >>> float(32)
    32.0
    >>> float('3.14159')
    3.14159

    最后,str将参数转化成字符串:

    >>> str(32)
    '32'
    >>> str(3.14159)
    '3.14159'

    数学函数

    Python有一个数学模块,他提供了大部分常见的数学函数。模块是一个包含相关函数的文件。

    在使用之前,必须先加载它:

    >>>import math

    上面的语句创建一个名叫math的模块对象。如果你print 模块对象,可以得到一些相关信息:

    >>> print math
    <module 'math' (built-in)>

    模块对象包含其中定义的函数和变量。使用模块中的函数,必须写出模块名称和函数名称,它们中间以点隔开。这种格式叫做圆点记法

    >>> ratio = signal_power / noise_power
    >>> decibels = 10 * math.log10(ratio)
    >>> radians = 0.7
    >>> height = math.sin(radians)

    第一个例子用log10计算分贝的信噪比(假设singal_power和noise_power已经定义)。math模块也提供了log,它计算以e为底的对数。

    第二个例子是计算弧度的余弦值。参数变量名,说明了sin或者其他三角函数(cos,tan,等)的参数是弧度。除以360乘以2pi可以把角度转化为弧度:

    >>> degrees = 45
    >>> radians = degrees / 360.0 * 2 * math.pi
    >>> math.sin(radians)
    0.707106781187

    结构

    目前为止,我们接触到的程序元素——变量、表达式和语句——都是孤立的,没有说如何将它们组合在一起。

    程序语言一项重要的特性便是包括一些小的代码块并何以组合他们。例如,函数的参数可以是任何包括算术操作符的表达式:

    x = math.sin(degrees / 360.0 * 2 * math.pi)

    甚至是函数调用:

    x = math.exp(math.log(x+1))

    几乎所有需要一个值的地方都可以用表达式替代,除了赋值语句的左边,它必须是一个变量名。任何表达式放在左边都会导致语法错误(也有例外的情况,将在后面讲到)。

    >>> minutes = hours * 60 # right
    >>> hours * 60 = minutes # wrong!
    SyntaxError: can't assign to operator

    自定义函数

    目前,我们只用到Python自带的函数,但也可以自己编写一个函数。函数定义指出函数的名称和函数调用时需要执行的代码。

    如下面的例子:

    def print_lyrics():
    print "I'm a lumberjack, and I'm okay."
    print "I sleep all night and I work all day."

    def 是定义函数的关键字。函数的名称是 print_lyrice。函数名称的规则和变量名规则一样:字母、数字和一些符号是合法的,但是第一个字符不能是数字。你可以使用关键字作为函数名,但应该避免变量和函数使用同样的名字。

    函数名后面的括号为空,表示该函数没有参数。

    函数定义的第一样叫做头部;其余的部分叫做函数体。头部以冒号结尾,函数体必须缩进,缩进始终都是4个空格;见“调试部分”。函数体可以保护任意数量的语句。

    print 语句中的字符串被双引号括住。单引号和双引号的作用一样,大部分情况下使用单引号,除非像上面这样,字符串中包含了单引号(也叫撇号)。

    如果在交互环境中定义函数,解释器会输出省略号(...)提示,定义是否完成:  //试了一下win7 2.7并没有...

    >>> def print_lyrics():
    ... print "I'm a lumberjack, and I'm okay."
    ... print "I sleep all night and I work all day."
    ...

    在函数的末尾,必须输入一个空行(在脚本里面,不需要这么做)。

    函数定义会产生一个同名变量。

    >>> print print_lyrics
    <function print_lyrics at 0xb7e99e9c>
    >>> type(print_lyrics)
    <type 'function'>

    print_lyrice的值是函数对象,它的类型是'function'。

    调用自定义函数的语法和内建函数一样:

    >>> print_lyrics()
    I'm a lumberjack, and I'm okay.
    I sleep all night and I work all day.

    定义一个函数以后,可以在其他函数里面使用他,例如,重复上面的函数两次,我们可以写一个叫做repeat_lyrice的函数:

    def repeat_lyrics():
        print_lyrics()
        print_lyrics()

    然后调用repeat_lyrice:

    >>> repeat_lyrics()
    I'm a lumberjack, and I'm okay.
    I sleep all night and I work all day.
    I'm a lumberjack, and I'm okay.
    I sleep all night and I work all day.

    可惜歌曲不是这么唱的。

     定义和使用

    将上面的几段代码放在一起,会成下面的样子:

    def print_lyrics():
        print "I'm a lumberjack, and I'm okay."
        print "I sleep all night and I work all day."
    
    def repeat_lyrics():
        print_lyrics()
        print_lyrics()
    
    repeat_lyrics()

    这段代码包含两个函数定义:print_lyrice 和 repeat_lyrice。定义函数再执行和其他代码一样,只是创建了一个函数对象。函数里面的语句不会执行,直到调用它,一般函数定义也没有输出。

    如你所想,你必须在函数执行之前创建它。也就是说,函数定义的语句必须在首次函数调用之前执行。

    练习 3-1

    把上面程序的最后一行移动到第一行,这样函数调用出现在定义前面。运行程序,看看错误提示是什么。

    //NameError: name 'repeat_lyrics' is not defined

    练习 3-2

    把函数调用语句放到最后,把print_lyrice定义放在repeat_lyrice后面,运行会发生什么。

    //成功

    运行流程

    为了保证函数首次调用之前已经定义,你必须了解代码运行顺序,叫做,运行流程

    程序始终从代码的第一行开始执行。语句从上到下按顺序执行。

    函数定义不会改变代码执行顺序,但是记住,函数定义中的代码不会执行,知道它被调用。

    函数调用就想运行流程绕了弯。不是执行下一行代码,而是跳到函数体,执行函数体的所有代码,然后回到刚刚的地方。

    这听起来很简单,但是一个函数调用另外一个就不简单了。在函数体中间,程序或许会执行另外一个程序中的代码。然而在执行这个函数的过程中,或许会再去执行另外一个函数的代码。

    幸运的是,Python可以很好的记住运行到哪一行,函数每次调用完成,程序继续回到刚刚没有完成的并被调用的函数。运行完所有代码,便结束。

    说明了什么?当你阅读一个程序的时候,不用每次都从第一行到最后一行。有些时候按照运行流程来阅读会更容易理解。

    形参和实参

    我们看到一些内建函数需要实参。例如,调用math.sin必须传递一个数值作为实参。一些函数不止一个实参,例如math.pow有两个,基数和指数。

    在函数里面,实参被复制给形参变量。这里有一个例子,一个用户自定义的函数有一个参数:

    def print_twice(bruce):
        print bruce
        print bruce

    这个函数把实参传递给形参变量bruce.当函数调用的时候,打印这个形参两次。

    任何可以被打印的值,函数都可以运行。

    >>> print_twice('Spam')
    Spam
    Spam
    >>> print_twice(17)
    17
    17
    >>> print_twice(math.pi)
    3.14159265359
    3.14159265359

    与结构同样的规则可以应用于内建函数和自定义函数,所以,我么可以用任何表达式作为print_twice的实参。

    >>> print_twice('Spam '*4)
    Spam Spam Spam Spam
    Spam Spam Spam Spam
    >>> print_twice(math.cos(math.pi))
    -1.0
    -1.0

    参数会在函数调用之前计算,所以在上面的例子中,'Span '*4 和math.cos(math.pi)只计算了一次。

    也可以使用变量作为函数的实参:

    >>> michael = 'Eric, the half a bee.'
    >>> print_twice(michael)
    Eric, the half a bee.
    Eric, the half a bee.

    (略)

    变量和形参是局部的

    在函数内部定义一个变量,它是局部的,也就是说它只在函数内部有效,例如:

    def cat_twice(part1, part2):
        cat = part1 + part2
        print_twice(cat)

     这个函数有两个参数,将它们连接起来,并打印两次,这里有一个运行的例子:

    >>> line1 = 'Bing tiddle '
    >>> line2 = 'tiddle bang.'
    >>> cat_twice(line1, line2)
    Bing tiddle tiddle bang.
    Bing tiddle tiddle bang.

    当cat_twice运行结束,变量cat被回收。如果我们尝试打印它,会报错:

    >>> print cat
    NameError: name 'cat' is not defined

    形参也是局部变量。例如,函数print_twice外部,不存在bruce变量。

    栈图

    为了明白每个变量在什么地方可用,有时候画一个栈图非常有用。就像程序流程图,栈图指明每个变量的值,也指明变量属于哪个函数。

    每个函数用一个框架表示。框架是一个旁边为函数名的方框,方框里面是函数的参数和变量名。上面的例子的栈图如图3-1.

    框架被分配在栈内存,用来表明哪个函数调用哪个,等等。在这个例子中,print_twice被cat_twice调用,cat_twice被__main__调用,__main__是顶层框架的特殊名称。如果创建一个变量,在任何函数外部,它属于__main__。

    每一个形参指向形参相同的值。因此,part1的值和line1相同,part2的值和line2相同,bruce的值和cat相同。

    如果函数调用的过程中发生错误,Python打印函数名称和调用它的函数名称,以及调用改函数的函数名称,如此下去,到__main__为止。

    例如,你尝试在print_twice中使用cat,将会产生一个变量名错误:

    Traceback (innermost last):
      File "test.py", line 13, in __main__
        cat_twice(line1, line2)
      File "test.py", line 5, in cat_twice
        print_twice(cat)
      File "test.py", line 9, in print_twice
        print cat
    NameError: name 'cat' is not defined

    这一串函数叫做回溯。它告诉你哪个程序文件的哪一行,执行哪个函数时发生错误。展示发生错误的哪一行代码。

    回溯的函数的排序和栈图中框架的排序一样。错误发生时,正在运行的函数列在最下方。

    有返回值和没有返回值的函数

    一些函数我们用于产生结果,例如数学函数;因为没有更好的名称,我们把它叫做有返回值的函数。另外一些函数,例如print_twice,会运算,但是没有返回值。它们叫没有返回值的函数

    当你调用一个有返回值的函数时,大多是想使用函数结果;例如,将它复制给一个变量或者用它作为表达式的一部分:

    x = math.cos(radians)
    golden = (math.sqrt(5) + 1) / 2

    如果在交互环境调用函数,Python会展示函数结果:

    >>> math.sqrt(5)
    2.2360679774997898

    但是,在脚本中,如果仅仅调用一个有返回值的函数,返回值会丢掉!

    math.sqrt(5)

    这个脚本计算了5的平方根,但是没有保存或者展示结果,因此它并没有用。

     没有返回值的函可能会在屏幕上输出,或者有一些其它效果,但是没有返回值。如果你尝试将结果复制给某个变量,会得到一个特殊的值,叫做None。

    >>> result = print_twice('Bing')
    Bing
    Bing
    >>> print result
    None

    None值和字符串'None'不同。它是一个特殊的值,有自己的类型:

    >>> print type(None)
    <type 'NoneType'>

    目前我们写的所有函数都是没有参数的。在后面一些章节中,会开始写有参数的函数。

    为什么使用函数?

    或许还不清楚,为什么值得,麻烦把程序划分成函数。有几个原因:

    • 创建函数可以将一段代码命名,这样利于程序阅读和调试。
    • 让程序看起来更简洁,因为去掉重复代码。另外,如果需要改动它,只需要改动一次。
    • 将很长的程序划分成函数,可以让你只调试某一段代码,调试好后再将它们放在一起运行。
    • 设计良好的函数可以应用于多个程序。写好并调试好一个函数,可多次使用。

    使用from导入

    Python提供两种方式导入模块;我们已经见过一种:

    >>> import math
    >>> print math
    <module 'math' (built-in)>
    >>> print math.pi
    3.14159265359

    如果导入math,会得到一个名叫math的模块对象。模块对象包含常数,例如pi,和函数,例如,sin和exp。

    但是如果你想直接使用pi,会得到一个报错。

    >>> print pi
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    NameError: name 'pi' is not defined

     另一种方法,你可以从模块中导入对象,例如:

    >>> from math import pi

    现在可以直接使用pi了,不需要点标记。

    >>> print pi
    3.14159265359

    或者,可以使用星号(*)导出模块的所有内容:

    >>> from math import *
    >>> cos(pi)
    -1.0

     导入math模块所有内容的好处是代码会变得更简洁。坏处是不同模块的命名可能会发生冲突,或者模块与工作环境中定义的变量名冲突。

    调试

    如果使用文本编辑器写脚本,可能运行时会由于使用了空格和制表符出错。避免的最好方法是只使用空格(不使用制表符)。很多文本编辑器知道Python,默认会这样做,但是一些不会。

    制表符和空格都是看不见的,因此很难调试,所以,为自己找一个便于缩进的编辑器。

    在运行之前,不要忘记保存程序。一些开发环境会自动保存,但一些不会。这样,有可能编辑器里面你正在看着的程序和实际运行的不一样。

    调试可能会花很长时间,如果你一直运行这个一样的,不正确的程序。

    保证你看到的代码就是运行的。如果不确定,输入一些类似  print 'Hello' 的代码在程序最前面,再次运行。如果没有看见hello,说明没有正确运行代码。 

    词汇表

    函数:

      一段被命名的语句,用来执行某个操作。函数可能有参数和没有参数,也可能有返回值和没有返回值。

    函数定义: 

      创建一个新函数的语句,指明函数的名称,参数,和要执行的代码。

    函数对象:

      函数定义创建的一个值。函数名是一个指向函数对象的变量。

    (函数)头:

      函数定义的第一行

    (函数)体:

      函数定义中的一段语句。

    形参:

      函数中使用的一个变量名,指向实参传递过来的值。

    函数调用:

      执行函数的代码。由函数名称和跟在后面的实参列表组成。

    实参:

      函数执行时传递给其的一个值。这个值传递给函数定义时相应的形参。

    局部变量:

      函数内部定义的变量。局部变量仅能在函数内部使用。

    返回值:

      函数的结果。如果函数作为一个表达式调用,那么返回值也是表达式的值。

    有返回值的函数:

      一个有返回值的函数。

    没有返回值的函数:

      不返回任何值的函数。

    模块:

      一个文件,它包含了相关的函数和一些其他定义。

    导入语句:

      一个语句,可以读取模块文件和创建模块对象。

    模块对象:

      import语句创建的一个值,可以通过它访问模块中定义的值。

    圆点记法:

      一种调用模块中函数的语法,写出模块的名称,跟一个点(实心句号),然后再写函数的名称。

    composition:

      用表达式作为另外一个更大的表达式的一部分,或者语句作为另外一个更大语句的一部分。

    运行流程:

      程序运行的过程中,语句运行的顺序。

    栈图:

      通过图表示函数的栈,包括变量,和变量指向的值。

    框架:

      栈图中用来表示一个函数调用的方框。包括局部变量和函数的形参。

    回溯:

      运行的函数列表,当错误发生时输出。

  • 相关阅读:
    设计模式之策略模式
    assert断言——调试中不应该是syso
    Spring AOP
    MyBatis
    事务处理与使用连接池管理连接
    管理结果集(ResultSet)
    执行SQL语句的方式
    JDBC基础:
    NIO.2
    NIO
  • 原文地址:https://www.cnblogs.com/zhaoxy/p/5005066.html
Copyright © 2020-2023  润新知