python 学习笔记(3)——函数和模块

python 学习笔记(3)——函数和模块

Contents

函数和模块

函数

函数定义

在 Python 中可以使用 def 关键字来定义函数,同样是用 return 关键字返回一个值(这里真的不要再有 return(a) 这种写法了!)。

一个简单的应用如下:(来自原文)

def factorial(num):
    """求阶乘"""
    result = 1
    for n in range(1, num + 1):
        result *= n
    return result


m = int(input('m = '))
n = int(input('n = '))
# 当需要计算阶乘的时候不用再写循环求阶乘而是直接调用已经定义好的函数
print(factorial(m) // factorial(n) // factorial(m - n))

这里的函数就是求取阶乘的函数,这个程序也就是在计算 $\dbinom{m}{n}$。

当然实际上很多简单的函数 Python 的各种模块中已经有了,所以实际应用中往往不会选择自己写。

函数参数

Python 对于函数参数的处理比较特别。

Python 中,函数参数可以有默认值,也支持使用可变参数。

比如:

from random import randint

def rand(n = 10):
    return randint(1, n)

print(rand(5))

print(rand())

其中第二次调用就是表示 $n$ 取默认值 $10$ 的调用。

再比如:(来自原文)

# 在参数名前面的*表示args是一个可变参数
def add(*args):
    total = 0
    for val in args:
        total += val
    return total


# 在调用add函数时可以传入0个或多个参数
print(add())
print(add(1))
print(add(1, 2))
print(add(1, 2, 3))
print(add(1, 3, 5, 7, 9))

这种写法是 C/C++ 等其他一些语言不那么容易实现的。

模块

用模块管理函数

对于一种编程语言(其实不止编程)来说,起名字很让人头疼。

最简单的就是同一个 .py 文件中定义了多个同名函数,这会导致后面的定义覆盖前面的。

这导致同名函数只能存在一个。

所以这个时候我们就可以通过模块的管理来解决这个问题。

我们有两个模块:

module1.py

def foo():
    print('bb is not here.')

module2.py

def foo():
    print('Thorin is not here.')

然后在 test.py 中可以这样使用:

from module1 import foo

# 输出 bb is not here.
foo()

from module2 import foo

# 输出 Thorin is not here.
foo()

也可以这样:

import module1 as m1
import module2 as m2

m1.foo()
m2.foo()

特别说明一点,如果导入的模块中除了定义函数之外,还有可执行代码,那么解释器导入模块时,就会执行这些代码,但是这往往是与我们希望相悖的。所以如果有这种可执行代码,最好加入如下判断:(来自原文)

def foo():
    pass


def bar():
    pass


# __name__是Python中一个隐含的变量它代表了模块的名字
# 只有被Python解释器直接执行的模块的名字才是__main__
if __name__ == '__main__':
    print('call foo()')
    foo()
    print('call bar()')
    bar()

说明:这里的 pass 语句只是一个占位语句,不做任何事情,一般用于填充尚未想好怎么实现的程序部分。(比如空函数中写 pass 保证不报错)

变量作用域(原文重点)

def foo():
    b = 'hello'

    # Python中可以在函数内部再定义函数
    def bar():
        c = True
        print(a)
        print(b)
        print(c)

    bar()
    # print(c)  # NameError: name 'c' is not defined


if __name__ == '__main__':
    a = 100
    # print(b)  # NameError: name 'b' is not defined
    foo()

首先注意函数内部定义函数的特殊用法。

上面代码可以顺利输出 100helloTrue 的结果,然而,在函数 bar(),我们实际上并没有定义过 ab

上面代码中 if 语句下定义了一个变量 a,这是一个全局变量(global variable),属于全局作用域,因为其没有定义在任何的函数中。在上面的 foo() 函数中定义了变量 b,这是一个定义在函数中的局部变量(local variable),属于局部作用域,在本函数外部无法访问。而对于 foo() 函数内部的 bar() 函数来说,变量 b 属于嵌套作用域,在 bar() 函数中依然可以访问。而 bar() 函数中的变量 c 属于局部作用域,外部都无法访问。

事实上,Python 查找一个变量会按照局部作用域、嵌套作用域、全局作用域和内置作用域的顺序进行搜索,所谓内置作用域就是 Python 内置的标识符,之前用过的 inputprintint 都属于内置作用域。

但注意,下面程序中,函数是新定义了一个局部变量而不是修改了全局变量:

def foo():
    a = 200
    print(a)  # 200


if __name__ == '__main__':
    a = 100
    foo()
    print(a)  # 100

如果想要在函数中改变全局变量而不是新建,需要使用 global 关键字。

函数需要修改为:

def foo():
    global a
    a = 200
    print(a)  # 200

这代表使用 global 关键字来指示 foo() 函数中的变量 a 来自于全局作用域,如果全局作用域中没有定义 a,后面就会新定义一个变量 a 并设置其作用域为全局作用域。同样还可以使用 nonlocal 关键字来指示变量来自于嵌套作用域。

一般实际开发中,应该尽量减少全局变量使用,其诸多好处就不说了。

想要延长函数中局部变量的生命周期需要使用闭包,当然这个这里不会讲。

最后作者给了一个良好的写法习惯:

def main():
    # Todo: Add your code here
    pass


if __name__ == '__main__':
    main()

可以学习一下。

原文“精选”习题

  1. 使用辗转相除法和递归求取两数的最大公约数:此处不提供参考程序。
  2. 写一个程序判断输入的正整数是不是回文素数:请用两个函数分别判断是否是素数和是否是回文数。

 

点赞 0

No Comments

Add your comment