函数是组织好的, 可重复使用的, 用来实现单一, 或相关联功能的代码段.函数能提高应用的模块性和代码的重复利用率.
你已经知道Python提供了许多内建函数, 比如print(). 但你也可以自己创建函数, 这被叫做用户自定义函数.
函数的创建和调用
函数的创建
创建函数也叫做定义函数, 在Python中, 定义函数使用def关键字, 语法如下:
def function_name([parameter_list]):
['''comments''']
[function_body]# 函数体
# 函数体可以包含任意多的语句
# 函数体的返回值可以是任何类型, 也可以没有返回值
function_name
是函数的名称, 应该见名知意, 但不能与Python的关键字和已有的函数名冲突.parameter_list
是可选参数, 如果有多个参数, 则用逗号分隔, 如果不指定, 则表示没有参数, 在调用时也不指定参数.comments
是可选参数, 表示为函数指定注释, 其内容通常是用来描述函数的功能和用法等. 指定后在调用时候可以用help(函数名)
或者函数名__doc__
获取.function_body
是函数体, 即函数的主体, 它包含了函数执行的语句, 它是函数的核心部分.如果函数有返回值, 则可以用return
语句返回.
注意函数体”function_body”和注释””’comments”'”相对于def关键字必须缩进.
函数的调用
函数创建完成后, 就可以调用函数了. 调用函数的语法如下:
function_name([argument_list])
function_name
是函数的名称, 要调用的函数必须是已经定义过的函数.argument_list
是可选参数, 它是调用函数时传入的参数, 如果有多个参数, 则用逗号分隔. 如果没有参数, 则直接写一个小括号即可.
例如计算圆的面积:
def area_of_circle(radius):
'''计算圆的面积'''
area = 3.14 * radius * radius # 计算圆的面积
return area # 返回圆的面积
# 调用函数
print(area_of_circle(5)) # 输出结果: 78.5
参数传递
在函数调用时, 传入的参数会被赋值给函数的形参. 因此, 函数内部可以直接使用这些参数, 也可以对参数进行修改, 但修改后的参数不会影响到函数外的变量.
了解形式参数和实际参数
形式参数(formal parameter)是函数定义时的参数, 实际参数(actual parameter)是函数调用时传入的参数.
根据实际参数的类型, 可以分为两种情况:
- 一种是将实际参数的值传递给形式参数;
- 另一种是将实际参数的引用(地址)传递给形式参数.
当实际参数是不可变对象(如数字, 字符串, 元组)时, 进行的是值传递, 即将实际参数的值复制给形式参数. 因此, 修改形式参数的值不会影响实际参数的值.
当实际参数是可变对象(如列表, 字典)时, 进行的是引用传递, 即将实际参数的引用(地址)复制给形式参数. 因此, 修改形式参数的值会影响实际参数的值.
实际上python中一切都是对象, 严格来讲不能说值传递还是引用传递, 而应该说传可变对象还是传不可变对象.
我们可以用id()
函数来查看对象的地址:
def change(a):
print(id(a)) # 指向的是同一个对象
a=10
print(id(a)) # 一个新对象
a=1
print(id(a))
change(a)
输出:
140719035398584
140719035398584
140719035398872
在定义函数时, 参数列表中的参数就是形式参数, 在调用时传递进来的参数就是实际参数, 如果说剧本中剧本的角色是形式参数, 那么演员是实际参数, 函数调用就是剧本的演出过程.
def demo(obj):
print('原值: ', obj)
obj+=obj
print('---传递不可变对象---')
string='hello'
demo(string)
print('修改后: ', string)
print('---传递可变对象---')
list1=[1,2,3]
demo(list1)
print('修改后: ', list1)
输出:
---传递不可变对象---
原值: hello
修改后: hello
---传递可变对象---
原值: [1, 2, 3]
修改后: [1, 2, 3, 1, 2, 3]
参数
以下是调用函数时可使用的正式参数类型:
- 位置参数(必需参数)
- 关键字参数
- 默认参数
- 不定长参数
位置参数
位置参数也叫必须参数, 必须按照正确的顺序传入函数, 即调用时的数量和位置必须与函数定义时的参数顺序一致.
1.数量必须和定义时一致
在调用函数时, 必须传入正确数量的参数, 否则会抛出TypeError异常.
2.位置必须正确
在调用函数时,指定的实际参数的位置必须与形式参数的位置一致, 否则会出现以下两种结果
- 抛出TypeError异常:实际参数的位置与形式参数的位置不一致, 且两种类型不能正常转换;
- 产生的结果与预期不符:实际参数的位置与形式参数的位置不一致, 但它们的数据类型一致.
关键字参数
关键字参数是指使用形式参数的名字来确定输入的参数值, 关键字参数在函数调用时可以用关键字来指定参数的值, 关键字参数可以不按顺序传入, 也可以按任意顺序传入.
#可写函数说明
def printinfo( name, age ):
"打印任何传入的字符串"
print ("名字: ", name)
print ("年龄: ", age)
return
#调用printinfo函数
printinfo( age=20, name="Shiqi" )
运行结果:
名字: Shiqi
年龄: 20
默认参数
默认参数是指在函数定义时, 没有传入相应的参数时, 函数会使用默认参数的值. 默认参数必须指向不变对象, 否则会出现不可预料的结果.
定义带有默认参数的函数的语法格式如下:
def function_name([parameter_list], [default_parameter_list]):
['''comments''']
[function_body]
function_name
是函数的名称.parameter_list
是可选参数列表, 它包含了函数定义时必须的参数, 多个参数用逗号分隔.default_parameter_list
是默认参数列表, 它包含了默认参数的名称和值, 多个默认参数用逗号分隔.comments
是可选参数, 表示为函数指定注释, 其内容通常是用来描述函数的功能和用法等.function_body
是函数体, 即函数的主体, 它包含了函数执行的语句, 它是函数的核心部分.
注意:
- 默认参数必须指向不可变对象, 否则会出现不可预料的结果.
- 默认参数必须在参数列表的最后, 否则会出现语法错误.
def add(a, b=10):
return a + b
print(add(2)) # 输出结果: 12
print(add(2, 3)) # 输出结果: 5
不定长参数
不定长参数也叫可变参数, 即传入函数的实际参数可以是零个或任意个.
定义可变参数时候主要有两种形式: 一种是*args
, 另一种是**kwargs
.
*args
*args
是可变参数, 它收集函数调用时的所有实际参数, 它是一个元组.
基本语法格式如下:
def functionname([formal_args,] *var_args_tuple ):
"函数_文档字符串"
[function_body]
定义一个函数, 它可以接受任意数量的位置参数, 并将它们组成元组:
def my_function(*args): #定义输出我博客的好友名称的函数
print("\n输出博客的好友名称: ")
for arg in args:
print(arg)
# 调用三次my_function函数, 分别指定不同多个的实际参数
my_function("wztxy")
my_function("wztxy", "IRIS", "Shigure", "Charactex")
my_function("wztxy", "IRIS", "Shigure")
输出结果为:
输出博客的好友名称:
wztxy
输出博客的好友名称:
wztxy
IRIS
Shigure
Charactex
输出博客的好友名称:
wztxy
IRIS
Shigure
如果想要使用一个已经存在的列表作为参数, 可以在列表的名称前加上*
号, 这样列表中的元素会被自动解包为位置参数:
my_list = ["wztxy", "IRIS", "Shigure", "Charactex"]
my_function(*my_list)
输出结果为:
输出博客的好友名称:
wztxy
IRIS
Shigure
Charactex
**kwargs
**kwargs
是关键字参数, 它收集任意多个类似关键词参数一样显式赋值(即使用等号=
)的实际参数, 并将其放到一个字典中.
基本语法格式如下:
def functionname([formal_args,] **var_args_dict ):
"函数_文档字符串"
[function_body]
定义一个函数, 它可以接受任意数量的关键字参数, 并将它们组成字典:
def my_function(**kwargs): #定义输出我博客的好友名称的函数
print("\n输出某人的信息: ")
for key, value in kwargs.items():
print(key, ":", value)
# 调用三次my_function函数, 分别指定不同多个的关键字参数
my_function(name="Shiqi")
my_function(name="Shiqi", age=20, city="Beijing")
my_function(name="Shiqi", age=20)
输出结果为:
输出某人的信息:
name : Shiqi
输出某人的信息:
name : Shiqi
age : 20
city : Beijing
输出某人的信息:
name : Shiqi
age : 20
如果想要使用一个已经存在的字典作为参数, 可以在字典的名称前加上**
号, 这样字典中的元素会被自动解包为关键字参数:
my_dict = {"name": "Shiqi", "age": 20, "city": "Beijing"}
my_function(**my_dict)
输出结果为:
输出某人的信息:
name : Shiqi
age : 20
city : Beijing
最后, 虽然可以组合多达5种参数, 但不要同时使用太多的组合, 否则函数接口的可理解性很差!
返回值
为函数设置返回值的作用是, 让函数的调用者能够获取函数执行后的结果.
在Python中, 可以在函数体内使用return
语句来返回函数的结果.
函数的返回值可以是任何类型, 并且无论return
语句在函数的什么位置, 只要执行到return
语句, 函数就立即结束执行, 并返回结果.
如果函数没有return
语句, 或者return
语句后面没有任何表达式, 则默认返回None
.
return
的语法格式如下:
return [value]
- return为函数指定返回值后, 在调用函数时可以把结果赋值给一个变量, 用于保存函数的返回值. 如果返回的一个值, 那么保存的就是返回的这个值, 可以是任意类型; 如果返回多个值, 那么保存的就是一个元组.
- value是可选参数, 它是函数的返回值, 可以是任意类型, 可以返回一个值, 也可以返回多个值.
# 模拟顾客结账
def checkout(money):
'''
计算顾客结账金额并进行折扣处理
money: 保存商品金额的列表
返回商品的合计金额
'''
money_old = sum(money)
money_new=money_old
if money_old >= 100 and money_old < 500:
money_new = money_old * 0.9
elif money_old >= 500 and money_old < 1000:
money_new = money_old * 0.8
elif money_old >= 1000:
money_new = money_old * 0.7
return money_old, money_new
# 调用函数
money1 = [100, 200, 300, 400, 500]
money2 = [1000, 300, 400, 500]
money3 = [500, 650, 1000]
total_old, total_new = checkout(money1) # 调用函数并保存返回值(元组的解包)
print("原价: ", total_old,"应付: ", total_new)
total_old, total_new = checkout(money2)
print("原价: ", total_old,"应付: ", total_new)
total_old, total_new = checkout(money3)
print("原价: ", total_old,"应付: ", total_new)
输出结果为:
原价: 1500 应付: 1050.0
原价: 2200 应付: 1540.0
原价: 2150 应付: 1505.0
变量的作用域
变量的作用域是指变量的有效范围, 它决定了变量的生命周期, 以及变量的作用范围.一般会根据变量的有效范围将变量分为局部变量和全局变量.
局部变量
局部变量是指在函数内部定义和使用的变量, 它只能在函数内部访问, 外部不能访问.
在函数内部定义的变量, 其作用域只在函数内部, 它在函数执行完毕后就被销毁.
如果在函数外部使用函数内部定义的变量, 则会出现NameError
异常.
全局变量
与局部变量对应, 全局变量为能够作用于函数内外部的变量, 主要有以下两种情况:
函数外定义的全局变量
如果一个变量是在函数外部定义的, 那么不仅可以在函数内部访问, 也可以在函数外部访问.
函数内定义的全局变量
如果一个变量是在函数内部定义的, 但是又需要在函数外部访问, 则需要使用global
关键字声明全局变量.
在函数外部也可以访问到该变量, 并且还可以再函数内部进行修改.
尽管Python中允许全局变量和局部变量重名, 但建议不要这么做, 因为会造成命名空间的混乱, 容易造成变量的混淆.
高阶函数
在 Python 中,map()、reduce()、filter() 和 sorted() 是几个非常有用的内置高阶函数, 下面分别介绍它们的用法.
1. map()
函数
功能: map()
函数会根据提供的函数对指定序列做映射. 它将传入的函数依次作用到序列的每个元素, 并把结果作为新的迭代器返回.
语法:
map(function, iterable, ...)
function
:是一个函数, 用于对序列中的每个元素进行处理.iterable
:是一个或多个可迭代对象, 如列表,元组等.
示例代码:
# 定义一个将数字平方的函数
def square(x):
return x ** 2
# 定义一个列表
numbers = [1, 2, 3, 4, 5]
# 使用 map() 函数将列表中的每个元素平方
result = map(square, numbers)
# 将结果转换为列表
squared_numbers = list(result)
print(squared_numbers) # 输出: [1, 4, 9, 16, 25]
2. reduce()
函数
功能: reduce()
函数会对参数序列中元素进行累积. 它会将一个二元函数(接受两个参数的函数)作用于一个序列的元素, 将结果继续和下一个元素做累积计算.
语法:
reduce(function, iterable[, initializer])
function
: 是一个二元函数, 用于对序列中的元素进行累积操作.iterable
: 是一个可迭代对象, 如列表,元组等.initializer
: 可选参数, 如果提供了该参数, 则作为初始值参与累积计算.
注意: reduce()
函数在 Python 3 中已经从内置函数中移除, 需要从 functools
模块中导入.
示例代码:
from functools import reduce
# 定义一个将两个数相加的函数
def add(x, y):
return x + y
# 定义一个列表
numbers = [1, 2, 3, 4, 5]
# 使用 reduce() 函数计算列表中所有元素的和
result = reduce(add, numbers)
print(result) # 输出: 15
# 提供初始值
result_with_initial = reduce(add, numbers, 10)
print(result_with_initial) # 输出: 25
3. filter()
函数
功能: filter()
函数用于过滤序列, 过滤掉不符合条件的元素, 返回一个迭代器, 其中包含符合条件的元素.
语法:
filter(function, iterable)
function
: 是一个判断函数, 用于判断序列中的每个元素是否符合条件. 如果该函数返回True
,则保留该元素; 如果返回False
,则过滤掉该元素.iterable
: 是一个可迭代对象, 如列表, 元组等.
示例代码:
# 定义一个判断数字是否为偶数的函数
def is_even(x):
return x % 2 == 0
# 定义一个列表
numbers = [1, 2, 3, 4, 5]
# 使用 filter() 函数过滤出列表中的偶数
result = filter(is_even, numbers)
# 将结果转换为列表
even_numbers = list(result)
print(even_numbers) # 输出: [2, 4]
4. sorted()
函数
功能: sorted()
函数用于对可迭代对象进行排序, 返回一个新的已排序列表, 原对象不会被修改.
语法:
sorted(iterable, key=None, reverse=False)
iterable
: 是一个可迭代对象, 如列表,元组等.key
:可选参数, 是一个函数, 用于指定排序的依据.该函数接受一个参数,返回一个用于排序的值.reverse
:可选参数,是一个布尔值,如果为True
,则按降序排序;如果为False
,则按升序排序,默认值为False
.
示例代码:
# 对列表进行升序排序
numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
sorted_numbers = sorted(numbers)
print(sorted_numbers) # 输出: [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]
# 对列表进行降序排序
sorted_numbers_desc = sorted(numbers, reverse=True)
print(sorted_numbers_desc) # 输出: [9, 6, 5, 5, 5, 4, 3, 3, 2, 1, 1]
# 根据字符串长度对列表进行排序
words = ['apple', 'banana', 'cherry', 'date']
sorted_words = sorted(words, key=len)
print(sorted_words) # 输出: ['date', 'apple', 'cherry', 'banana']
匿名函数
匿名函数也叫做lambda函数, 它是一个表达式, 而不是一个函数. 它可以用来创建小型的函数, 但只能包含一个表达式.
- lambda 函数是匿名的, 它们没有函数名称, 只能通过赋值给变量或作为参数传递给其他函数来使用.
- lambda 函数通常只包含一行代码, 这使得它们适用于编写简单的函数.
匿名函数的语法格式如下:
lambda [arg1 [,arg2,.....argn]]:expression
arg1
到argn
是可选参数, 它们是函数的参数, 可以有多个参数.expression
是表达式, 它是匿名函数的主体, 它只包含一个表达式, 不能包含其他语句.
# 定义一个匿名函数
add = lambda x, y: x + y
# 调用匿名函数
print(add(2, 3)) # 输出结果: 5
匿名函数也可以配合map()
和filter()
函数一起使用, 用于对列表中的元素进行操作.
# 定义一个列表
my_list = [1, 2, 3, 4, 5]
# 使用匿名函数对列表进行过滤
my_list = list(filter(lambda x: x % 2 == 0, my_list))
print(my_list) # 输出结果: [2, 4]
# 使用匿名函数对列表进行映射
my_list = list(map(lambda x: x * 2, my_list))
print(my_list) # 输出结果: [4, 8]
装饰器
装饰器(decorators)是 Python 中的一种高级功能, 它允许你动态地修改函数或类的行为.
它是一种函数, 它接受一个函数作为参数, 返回一个修改过的函数.
Python 装饰允许在不修改原有函数代码的基础上, 动态地增加或修改函数的功能.
装饰器本质上是一个接收函数作为输入并返回一个新的包装过后的函数的对象.
装饰器的语法格式如下:
def decorator_function(original_function):
def wrapper(*args, **kwargs):
# 这里是在调用原始函数前添加的新功能
before_call_code()
result = original_function(*args, **kwargs)
# 这里是在调用原始函数后添加的新功能
after_call_code()
return result
return wrapper
# 使用装饰器
@decorator_function
def target_function(arg1, arg2):
pass # 原始函数的实现
decorator_function
是装饰器函数, 它接受一个函数作为参数, 返回一个包装过后的函数.original_function
是原始函数, 它是被装饰的函数.wrapper
是包装函数, 它是装饰器函数的返回值, 它包裹了原始函数, 在原始函数调用前后添加了一些额外的功能.before_call_code
和after_call_code
是可选参数, 它们是装饰器函数的前置和后置代码, 它们在原始函数调用前后执行.
当我们使用 @decorator_function 前缀在 target_function 定义前, Python会自动将 target_function 作为参数传递给 decorator_function, 然后将返回的 wrapper 函数替换掉原来的 target_function.
使用装饰器
装饰器通过 @ 符号应用在函数定义之前, 例如:
@time_logger
def target_function():
pass
等同于:
def target_function():
pass
target_function = time_logger(target_function)
下面是一个简单的装饰器示例:
# 定义一个装饰器
def my_decorator(func):
def wrapper():
print("装饰器开始运行...")
func()
print("装饰器结束运行...")
return wrapper
# 定义一个函数
def say_hello():
print("Hello, world!")
# 使用装饰器装饰函数
say_hello = my_decorator(say_hello)
# 调用函数
say_hello() # 输出结果: 装饰器开始运行... Hello, world! 装饰器结束运行...
递归函数
递归函数是指在函数内部调用自身的函数.
递归函数的特点是:
- 一个函数在内部调用自身.
- 递归函数必须有结束条件, 否则会导致栈溢出.
下面是一个简单的递归函数示例:
def factorial(n):
if n == 1:
return 1
else:
return n * factorial(n-1)
print(factorial(5)) # 输出结果: 120
在这个示例中, factorial()
函数是一个递归函数, 它接受一个参数 n
, 并返回 n
的阶乘.
- 如果
n
为 1, 则返回 1. - 如果
n
不为 1, 则返回n
乘以n-1
的阶乘.
因此, factorial(5)
等于 5 * factorial(4)
, 等于 5 * 4 * factorial(3)
, 等于 5 * 4 * 3 * factorial(2)
, 等等, 直到 n
为 1 时停止递归.
可以用递归函数实现斐波那契数列:
def fibonacci(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(6)) # 输出结果: 8
也可以用递归函数实现汉诺塔问题:
def hanoi(n, a, b, c):
if n == 1:
print(a, "->", c)
else:
hanoi(n-1, a, c, b)
print(a, "->", c)
hanoi(n-1, b, a, c)
hanoi(3, "A", "B", "C")
输出结果:
A -> C
A -> B
C -> B
A -> C
B -> A
B -> C
A -> C
事实上递归和循环区别并不大, 但递归更加适合处理树形结构的数据, 而循环更适合处理线性结构的数据.