在讲解本章节的内容之前,我们先来研究一道数学题,请说出下面的方程有多少组正整数解。
在这个数学问题中,我们需要求解一个方程有多少组正整数解。而这个方程实际上等同于将8个苹果分成四组,每组至少一个苹果,有多少种方案。这是一个经典的组合问题,可以通过数学方法求解。当我们意识到这一点后,问题的答案就呼之欲出了,让我们一起来尝试吧!
可以用Python的程序来计算出这个值,代码如下所示。
m = int(input('m = '))
n = int(input('n = '))
fm = 1
for num in range(1, m + 1):
fm *= num
fn = 1
for num in range(1, n + 1):
fn *= num
fm_n = 1
for num in range(1, m - n + 1):
fm_n *= num
print(fm // fn // fm_n)
函数的作用
这段文档中的代码中有重复的计算阶乘的代码。为了避免重复代码,我们可以将计算阶乘的功能封装到一个称之为“函数”的功能模块中,在需要计算阶乘的地方,我们只需要“调用”这个“函数”就可以了。
定义函数
在Python中可以使用def
关键字来定义函数。函数名可以根据变量命名规则进行定义,圆括号中可以放置传递给函数的参数。函数执行完成后我们可以通过return
关键字来返回一个值。
以下是重构后的代码:
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(n) // factorial(m) // factorial(n - m))
这段文档中的代码展示了如何在Python中定义函数。函数的定义以def关键字开头,后跟函数名和一组圆括号。函数的主体则包含在缩进块中。在函数内部,我们可以定义任意数量的变量、执行任意数量的语句和返回任意类型的值。在文档中的例子中,我们定义了一个名为factorial的函数,用于计算阶乘。了解了函数的定义后,我们就可以将计算阶乘的代码封装成函数,避免代码重复。
函数的参数
函数是绝大多数编程语言中都支持的一个代码的"构建块",但是Python中的函数与其他语言中的函数还是有很多不太相同的地方。其中一个显著的区别是Python对函数参数的处理。Python中的函数参数可以有默认值,也支持使用可变参数。这些特性使得Python不需要像其他语言一样支持函数的重载。
在Python中,我们可以为函数的参数设置默认值,这样当函数被调用时,如果没有传入该参数的值,函数会使用默认值。这种方式可以方便地处理一些常见的情况,例如函数中有一个可选参数。
除了默认参数外,Python还支持使用可变参数。可变参数允许我们定义一个函数,它可以接收任意数量的参数。这些参数被包装为一个元组或字典,然后传递给函数。这种方式可以方便地处理一些不确定参数数量的情况。
因为Python中的函数参数处理方式的灵活性,我们在定义一个函数的时候可以让它有多种不同的使用方式。下面是两个小例子:
from random import randint
def roll_dice(n=2):
"""摇色子"""
total = 0
for _ in range(n):
total += randint(1, 6)
return total
def add(a=0, b=0, c=0):
"""三个数相加"""
return a + b + c
# 如果没有指定参数那么使用默认值摇两颗色子
print(roll_dice())
# 摇三颗色子
print(roll_dice(3))
print(add())
print(add(1))
print(add(1, 2))
print(add(1, 2, 3))
# 传递参数时可以不按照设定的顺序进行传递
print(add(c=50, a=100, b=200))
我们在这段文本中定义了两个函数,一个带默认参数,一个带可变参数。这些特性使得Python中的函数与其他语言中的函数还是有很多不太相同的地方。Python中的函数参数可以有默认值,也支持使用可变参数。这些特性使得Python不需要像其他语言一样支持函数的重载。因此,在Python中我们可以让一个函数有多种不同的使用方式。
def add(*args):
"""
This function can take any number of arguments and returns their sum.
"""
result = 0
for arg in args:
result += arg
return result
上面的add
函数使用了可变参数,这样就可以接受任意数量的参数来进行加法运算。因为我们可能会对0个或多个参数进行加法运算,而具体有多少个参数是由调用者来决定,我们作为函数的设计者对这一点是一无所知的,因此在不确定参数个数的时候,我们可以使用可变参数。
用模块管理函数
对于任何一种编程语言来说,给变量、函数这样的标识符起名字都是一个让人头疼的问题,因为我们会遇到命名冲突这种尴尬的情况。最简单的场景就是在同一个.py文件中定义了两个同名函数,由于Python没有函数重载的概念,那么后面的定义会覆盖之前的定义,也就意味着两个函数同名函数实际上只有一个是存在的。
def foo():
print('hello, world!')
def foo():
print('goodbye, world!')
# 下面的代码会输出什么呢?
foo()
当然上面的这种情况我们很容易就能避免,但是如果项目是由多人协作进行团队开发的时候,团队中可能有多个程序员都定义了名为foo
的函数,那么怎么解决这种命名冲突呢?答案其实很简单,Python中每个文件就代表了一个模块(module),我们在不同的模块中可以有同名的函数,在使用函数的时候我们通过import
关键字导入指定的模块就可以区分到底要使用的是哪个模块中的foo
函数,代码如下所示。
module1.py
def foo():
print('hello, world!')
module2.py
def foo():
print('goodbye, world!')
test.py
from module1 import foo
# 输出hello, world!
foo()
from module2 import foo
# 输出goodbye, world!
foo()
也可以按照如下所示的方式来区分到底要使用哪一个foo
函数。
test.py
import module1 as m1
import module2 as m2
m1.foo()
m2.foo()
但是如果将代码写成了下面的样子,那么程序中调用的是最后导入的那个foo
,因为后导入的foo覆盖了之前导入的foo
。
test.py
from module1 import foo
from module2 import foo
# 输出goodbye, world!
foo()
test.py
from module2 import foo
from module1 import foo
# 输出hello, world!
foo()
需要说明的是,如果我们导入的模块除了定义函数之外还中有可以执行代码,那么Python解释器在导入这个模块时就会执行这些代码,事实上我们可能并不希望如此,因此如果我们在模块中编写了执行代码,最好是将这些执行代码放入如下所示的条件中,这样的话除非直接运行该模块,if条件下的这些代码是不会执行的,因为只有直接执行的模块的名字才是"__main__"。
module3.py
def foo():
pass
def bar():
pass
# __name__是Python中一个隐含的变量它代表了模块的名字
# 只有被Python解释器直接执行的模块的名字才是__main__
if __name__ == '__main__':
print('call foo()')
foo()
print('call bar()')
bar()
test.py
import module3
# 导入module3时 不会执行模块中if条件成立时的代码 因为模块的名字是module3而不是__main__
练习
练习1:实现计算求最大公约数和最小公倍数的函数。
练习2:实现判断一个数是不是回文数的函数。
练习3:实现判断一个数是不是素数的函数。
练习4:写一个程序判断输入的正整数是不是回文素数。