函数

[!NOTE]

以下内容部分摘录自《Python编程快速上手——让繁琐的工作自动化(第二版)》,并加入了一些自己的理解,仅用于个人交流与学习,如涉及侵权请联系站长删除!

1. 函数定义与调用

  • 语法:使用 def 关键字定义函数。

  • 参数:函数可以接收参数(变元)参数在函数内部作为局部变量使用。

  • 示例

    1
    2
    3
    4
    5
    def hello(name):
    print('Hello, ' + name)

    hello('GSY') # 输出:Hello, GSY
    hello('CYX') # 输出:Hello, CYX
  • 解释:像print()或者 len() 函数括号中经常带有一些值,他们称之为函数的参数,也可以自己定义接收到参数的函数。

    • 如同上述例子一样:hello函数中有一个名叫name的变元,当函数被调用时,参数就存放在其中(保存在变元中的值,在函数返回后就消失了,例如:在hello('CYX')之后再print(name)那么程序就会报错。)
  • 示例

    1
    2
    3
    def sayHello(abc):
    print('Hello, ' + abc)
    sayHello('AI')
  • 解释def sayHello(abc)就是定义了一个这样的函数,而sayHello('AI')行则是调用了这样的函数,这样的函数调用也将字符串里的AI 传递给该函数,传递给函数的值称为参数,这里的abc就是接受参数AI 的局部变量,也叫做变元。

2. 参数与返回值

  • 返回值:使用 return 语句指定函数的返回值。若无 return,默认返回 None

  • 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    import random
    def getAnswer(answerNumber):
    if answerNumber == 1:
    return 'It is certain'
    elif answerNumber == 2:
    return 'It is decidedly so'
    elif answerNumber == 3:
    return 'Yes'
    elif answerNumber == 4:
    return 'Replay hazy try again'
    elif answerNumber == 5:
    return 'Ask again later'
    elif answerNumber == 6:
    return 'Concentrate and ask again'
    elif answerNumber == 7:
    return 'My replay is no'
    elif answerNumber == 8:
    return 'Outlook not so good'
    elif answerNumber == 9:
    return 'Very doubtful'

    r = random.randint(1, 9)
    fortune = getAnswer(r)
    print(fortune) # 随机输出结果
  • 解释:这是一个随机返回一句话的例子,我们首先定义了一个函数getAnswer(),之后又生成了一个随机变元r它通过random函数在1-9之间随机一个数字,之后再通过对应我们定义函数中对应的部分来返回相应的句子,一般来说函数调用求值的结果称为函数的返回值,用def语句创建函数时可以用return语句指定应该返回什么值,return语句包含 :
    • 1.return关键字
    • 2.函数应该返回的值或者表达式(如果return语句中是有了表达式那么返回值就是求值的结果)

3. 关键字参数与 None 值

  • None 值:无显式返回值的函数默认返回 None。例如:print()函数,它直接在屏幕上显示文本,但不需要返回任何值,既然所有的函数调用都需要一个返回值,那么它的返回值就是None,而对于所有没有return语句的函数和不带值的return语句他们的返回值都默认是None

  • 关键字参数:通过参数名指定值,常用于可选参数。

  • 示例

    1
    2
    3
    4
    print('Hello', end=' ')  # 输出:Hello World(不换行)
    print('World')

    print('cats', 'dogs', 'mice', sep=',') # 输出:cats,dogs,mice
  • 解释:上面所展示的就是print()函数的两个可选的变元endsep
    • sep:指定参数间的分隔符,即:在参数之间输入什么来将他们分隔开(默认空格)。
    • end:指定末尾字符,即:指定在参数末尾输出什么(默认换行)。

4. 调用栈

  • 调用栈:Python 通过栈结构管理函数调用,每次调用函数时创建帧对象,返回时销毁。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def Alice():
    print('Alice starts')
    Bob()
    Carol()
    print('Alice returns')

    def Bob():
    print('Bob starts')
    Carol()
    print('Bob returns')

    def Carol():
    print('Carol starts')
    print('Carol returns')

    Alice()
  • 解释:调用栈是Python记住每个函数调用后在那里返回执行的方式,调用栈不是存储在程序的变量中而是由python在后台处理它,程序调用一个函数时python在调用栈的顶部创建一个帧对象帧对象保存了再出函数调用的行号,使得python可以记住返回的位置,如果进行了另一个函数的调用python会将另一个帧对象放在调用栈中,且在前一个帧对象之上,当函数调用返回时,python从栈顶部删除一个对象并将执行栈转移至保存在其中的行号。

    注意 :帧对象始终是从栈顶部添加和删除的。

  • 执行流程

    1. Alice() 调用 Bob()Carol()
    2. Bob() 调用 Carol()
    3. 函数返回时按栈顺序退出。
  • 代码输出

    1
    2
    3
    4
    5
    6
    7
    8
    Alice starts
    Bob starts
    Carol starts
    Carol returns
    Bob returns
    Carol starts
    Carol returns
    Alice returns

5. 作用域

  • 全局作用域:函数外定义的变量,处于全局作用域中的变量称为全局变量。

  • 局部作用域:函数内定义的变量,函数返回后销毁,处于局部作用域中的变量被称为局部变量。

  • 规则

    1. 全局作用域不能访问局部变量

      1
      2
      3
      4
      5
      def spam():
      eggs = 31337
      spam()
      print(eggs)
      # 注意:这是错误的代码!!!
      • 解释:发生错误是因为eggs变量只属于spam()调用所创建的局部作用域,在程序执行从spam返回后,该局部作用域就被销毁了。
    2. 不同函数的局部作用域相互独立

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      def spam():
      eggs = 99
      bacon()
      print(eggs)
      def bacon():
      ham = 101
      eggs = 0

      spam()
      # 这串代码的输出结果为99
      • 解释:这串代码第一个局部作用域来自于spam函数的调用,其中eggs的值为99,然后bacon函数被调用,创建了第二个局部作用域,在新的作用域中ham被赋值为101,eggs赋值为0,当bacon函数返回时,这次调用的局部作用域被销毁,程序执行在spam函数中继续输出eggs的值,所以eggs值为99被输出。
    3. 局部作用域可读取全局变量

      1
      2
      3
      4
      5
      def spam():
      print(eggs)
      eggs = 42
      spam()
      print(eggs)

      代码输出

      1
      2
      42
      42
      • 解释:首先我们调用的spam()中的print(eggs)处于函数spam()的局部作用域中,却可以输出我们在全局作用域中定义的eggs的值,这说明局部作用域可读取全局变量。
    4. 同名变量在不同作用域互不影响

6. global 语句

  • 作用:在函数内修改全局变量。

  • 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    eggs = 'abc'

    def spam():
    global eggs
    eggs = 'spam' # 修改全局变量

    spam()
    print(eggs)
    # 代码输出为:spam
  • 解释:用global语句来告诉python我们要在此函数内修改的eggs是一个全局变量,不要用这个名字创建一个局部变量。因为eggsspam()函数的顶部被声明为global所以当eggs被赋值为spam时赋值发生在全局作用域的eggs上没有创建局部eggs变量。

  • 法则(判断全局变量或局部变量)

    1.如果变量在全局作用域中使用(在所有函数之外),它就是全局变量。

    2.如果在一个函数中有针对该变量的global语句,它就是全局变量。

    3.如果该变量用于函数中的赋值语句,它就是局部变量。

    4.如果该变量没有用在赋值语句中,它就是全局变量。

7. 异常处理

  • 语法:使用 tryexcept 捕获异常。

  • 示例

    1
    2
    3
    4
    5
    6
    7
    def spam(divideBy):
    return 42 / divideBy

    print(spam(2))
    print(spam(12))
    print(spam(0))
    print(spam(1))

    注意:如果运行以上代码会遇到异常,因为有一个除数为0,遇到这种情况可以使用tryexcept语句来进行异常处理处理。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def spam(divideBy):
    try:
    return 42 / divideBy
    except ZeroDivisionError:
    print('Error: Invalid argument.')

    print(spam(2))
    print(spam(12))
    print(spam(0)) # 输出:Error: Invalid argument. 后返回 None
    print(spam(1))
  • 解释:我们使用tryexcept语句来检测代码中可能会报错的细节,但一旦跳到except子句的代码,就不会回到try的子句,而会继续向下执行。