Appearance
函数
函数是Python中的一种代码组织方式,用于实现特定的功能。
函数可以将代码模块化,便于代码重用和维护。
函数可以接受参数,并根据参数执行相应的操作。
函数可以返回结果,以便在其他地方使用。
定义函数
在Python中可以使用def
关键字来定义函数,和变量一样每个函数也有一个响亮的名字,而且命名规则跟变量的命名规则是一致的。
在函数名后面的圆括号中可以放置传递给函数的参数,这一点和数学上的函数非常相似,程序中函数的参数就相当于是数学上说的函数的自变量。
函数执行完成后我们可以通过return
关键字来返回一个值,这相当于数学上说的函数的因变量。
计算斐波那契数列的函数如下所示。
Python
def fibonacci(n):
if n <= 0:
return []
elif n == 1:
return [0]
elif n == 2:
return [0, 1]
sequence = [0, 1]
for _ in range(2, n):
sequence.append(sequence[-1] + sequence[-2])
return sequence
# 计算第3个斐波那契数
print(fibonacci(3)) # 输出: [0, 1, 1]
# 计算第4个斐波那契数
print(fibonacci(4)) # 输出: [0, 1, 1, 2]
# 计算第5个斐波那契数
print(fibonacci(5)) # 输出: [0, 1, 1, 2, 3]
函数的参数
函数是绝大多数编程语言中都支持的一个代码的"构建块",但是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))
我们给上面两个函数的参数都设定了默认值,这也就意味着如果在调用函数的时候如果没有传入对应参数的值时将使用该参数的默认值,所以在上面的代码中我们可以用各种不同的方式去调用add
函数,这跟其他很多语言中函数重载的效果是一致的。
其实上面的add
函数还有更好的实现方案,因为我们可能会对0个或多个参数进行加法运算,而具体有多少个参数是由调用者来决定,我们作为函数的设计者对这一点是一无所知的,因此在不确定参数个数的时候,我们可以使用可变参数,代码如下所示。
Python
# 在参数名前面的*表示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))
# 在参数名前面的**表示args是一个可变参数,当参数不确定有多少时使用它,会把不确定的部分存在**代表的字典中
def print_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
# 示例用法
print_info(name="Alice", age=30, city="New York")
指明参数类型和返回值类型
通过使用函数注解的方式来指定函数的参数类型。函数注解是在函数定义中给参数和返回值添加类型信息的一种方法,它并不会对实际的运行产生影响,仅仅是为了提供给人们阅读代码时更好的理解。
Python
from typing import List
def fibonacci(n: int) -> List[int]:
if n <= 0:
return []
elif n == 1:
return [0]
elif n == 2:
return [0, 1]
sequence = [0, 1]
for _ in range(2, n):
sequence.append(sequence[-1] + sequence[-2])
return sequence
# 计算第3个斐波那契数
print(fibonacci(3)) # 输出: [0, 1, 1]
# 计算第4个斐波那契数
print(fibonacci(4)) # 输出: [0, 1, 1, 2]
# 计算第5个斐波那契数
print(fibonacci(5)) # 输出: [0, 1, 1, 2, 3]
练习
练习1:实现计算求最大公约数和最小公倍数的函数。
参考答案:
Python
def gcd(x, y):
"""求最大公约数"""
(x, y) = (y, x) if x > y else (x, y)
for factor in range(x, 0, -1):
if x % factor == 0 and y % factor == 0:
return factor
def lcm(x, y):
"""求最小公倍数"""
return x * y // gcd(x, y)
练习2:实现判断一个数是不是回文数的函数。
参考答案:
Python
def is_palindrome(num):
"""判断一个数是不是回文数"""
temp = num
total = 0
while temp > 0:
total = total * 10 + temp % 10
temp //= 10
return total == num
练习3:实现判断一个数是不是素数的函数。
参考答案:
Python
def is_prime(num):
"""判断一个数是不是素数"""
for factor in range(2, int(num ** 0.5) + 1):
if num % factor == 0:
return False
return True if num != 1 else False
练习4:写一个程序判断输入的正整数是不是回文素数。
参考答案:
Python
if __name__ == '__main__':
num = int(input('请输入正整数: '))
if is_palindrome(num) and is_prime(num):
print('%d是回文素数' % num)
注意:通过上面的程序可以看出,当我们将代码中重复出现的和相对独立的功能抽取成函数后,我们可以组合使用这些函数来解决更为复杂的问题,这也是我们为什么要定义和使用函数的一个非常重要的原因。
变量的作用域
最后,我们来讨论一下Python中有关变量作用域的问题。
Python
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()
上面的代码能够顺利的执行并且打印出100、hello和True,但我们注意到了,在bar
函数的内部并没有定义a
和b
两个变量,那么a
和b
是从哪里来的。我们在上面代码的if
分支中定义了一个变量a
,这是一个全局变量(global variable),属于全局作用域,因为它没有定义在任何一个函数中。在上面的foo
函数中我们定义了变量b
,这是一个定义在函数中的局部变量(local variable),属于局部作用域,在foo
函数的外部并不能访问到它;但对于foo
函数内部的bar
函数来说,变量b
属于嵌套作用域,在bar
函数中我们是可以访问到它的。bar
函数中的变量c
属于局部作用域,在bar
函数之外是无法访问的。事实上,Python查找一个变量时会按照“局部作用域”、“嵌套作用域”、“全局作用域”和“内置作用域”的顺序进行搜索,前三者我们在上面的代码中已经看到了,所谓的“内置作用域”就是Python内置的那些标识符,我们之前用过的input
、print
、int
等都属于内置作用域。
再看看下面这段代码,我们希望通过函数调用修改全局变量a
的值,但实际上下面的代码是做不到的。
Python
def foo():
a = 200
print(a) # 200
if __name__ == '__main__':
a = 100
foo()
print(a) # 100
在调用foo
函数后,我们发现a
的值仍然是100,这是因为当我们在函数foo
中写a = 200
的时候,是重新定义了一个名字为a
的局部变量,它跟全局作用域的a
并不是同一个变量,因为局部作用域中有了自己的变量a
,因此foo
函数不再搜索全局作用域中的a
。如果我们希望在foo
函数中修改全局作用域中的a
,代码如下所示。
Python
def foo():
global a
a = 200
print(a) # 200
if __name__ == '__main__':
a = 100
foo()
print(a) # 200
我们可以使用global
关键字来指示foo
函数中的变量a
来自于全局作用域,如果全局作用域中没有a
,那么下面一行的代码就会定义变量a
并将其置于全局作用域。
匿名函数(lambda 表达式)
使用场景
需要一个函数,但是又不想费神去命名这个函数
通常在这个函数只使用一次的场景下
可以指定短小的回调函数
语法
result = lambda [arg1 [, arg2, .... , argn]]: expression
示例
python
# 常规写法
def circle_area(r):
"""
计算圆的面积
r:半径
"""
result = math.pi * r ** 2
return result
r = 10
print(f"半径为{r}的面积为{circle_area(r)}")
# lanbda表达式
# result = lambda 参数 : expression
result = lambda r: math.pi * r ** 2
print(f"半径为{r}的面积为{circle_area(r)}")