学习python的第十二天之函数——函数用法
def函数
Python 中的函数是一段组织好的、可重复使用的、用来实现单一或相关联功能的代码块。函数通过定义可以提高应用的模块性和代码的重复利用率。
目的:减少代码冗余,使一些重复的代码定义在函数中
场景:
- 代码重用:
- 通过定义函数,可以避免重复编写相同的代码。只需调用函数即可重复使用其功能。
- 模块化设计:
- 函数可以将代码分解为更小、更易于管理的模块。每个模块(函数)负责特定的任务,使得整体代码结构更加清晰。
- 抽象和封装:
- 函数提供了一种抽象层,隐藏了具体的实现细节,只暴露必要的接口。这有助于保护代码并简化复杂系统的开发。
- 参数化:
- 函数可以接受参数,使得它们能够处理不同的输入数据,从而增强了代码的灵活性和通用性。
- 递归:
- 函数可以递归调用自身,这是解决某些问题(如树遍历、图搜索、分治算法等)的有效方法。
- 事件处理:
- 在图形用户界面(GUI)编程或事件驱动的编程中,函数通常用作事件处理器,响应用户的输入或系统事件。
- 数据处理和分析:
- 在数据科学、机器学习和统计分析中,函数用于执行数据清洗、转换、分析和可视化等任务。
- 文件操作:
- 函数常用于封装文件读写操作,使得代码更加整洁,并减少错误发生的可能性。
- 网络编程:
- 在网络应用中,函数用于处理网络连接、数据传输和协议解析等任务。
- API 调用和集成:
- 函数常用于封装对外部API的调用,使得与第三方服务的集成更加简单和可靠。
- 测试和调试:
- 在单元测试和集成测试中,函数是测试的基本单元。通过编写测试函数,可以验证代码的正确性和稳定性。
- 插件和扩展:
- 在许多框架和系统中,函数用作插件或扩展点,允许开发者添加自定义功能。
- 回调函数:
- 在异步编程或事件处理中,回调函数用于在特定事件发生时执行特定的代码。
- 装饰器:
- Python 支持装饰器,这是一种特殊的函数,用于修改或扩展其他函数的行为。
- 配置和初始化:
- 函数常用于程序的配置和初始化过程,如设置数据库连接、加载配置文件等。
格式:
def functionName(parameters): """ 函数的文档字符串(可选) """ # 函数体 # 这里执行功能实现 return value # 返回值(可选)
def
是定义函数的关键字。functionName
是函数的名称,用于标识函数。parameters
是函数可接受的参数,用于传递和接收数据。参数是可选的;函数也可以不接受任何参数。- 函数体部分是函数被调用时,Python 将执行的代码块。
return
语句用于从函数中返回一个值。如果函数没有返回值或者没有写 return 语句,函数默认返回None
。
定义函数和调用
def add(a, b):
"""
这个函数接收两个参数并返回它们的和
"""
return a + b
# 调用函数
result = add(5, 3) # 调用定义的add函数
print(result) # 输出:8
函数的参数
Python 函数的参数可以是以下几种类型:int、str、float、bool、list、tuple、dict、set
例外,函数参数还可以传递函数!学习python的第十四天之函数——函数的作用域
- 必备参数:必须传递给函数,否则函数将无法调用。
- 关键字参数:使用关键字(即变量名)来指定传递给函数的值。
- 默认参数:为参数设置默认值,如果在调用函数时没有传递该参数,则使用默认值。
- 不定长参数:可以传递任意数量的参数,包括两种形式:
*args
(用于接收非关键字参数)和**kwargs
(用于接收关键字参数)。
*args
(argument)是用来接收任意数量的位置参数的,这些参数会被自动组装成一个元组(tuple)。**kwargs
(keyword argument) 是用来接收任意数量的关键字参数的。这些参数会被自动组装成一个字典(dictionary),其中键是参数名,值是对应的参数值。
函数的形参和实参
函数是组织代码、实现特定功能的重要结构。函数定义时涉及的参数称为形参(形式参数),而函数调用时传入的参数则称为实参(实际参数)。
-
形参(形式参数)
形参是函数定义时使用的变量,它们代表了函数调用时将要接收的值。形参在函数体内部使用,以执行特定的操作。
-
实参(实际参数)
实参是函数调用时实际传递给函数的值。这些值被赋给形参,以供函数在内部使用。
# 位置参数和默认参数,message默认'Hello!'
def greet(name, message="Hello!"):
print(f"{message}, {name}.")
greet("Alice") # 输出:Hello!, Alice. name是位置参数,message是默认参数
greet("Bob", "Hi!") # 输出:Hi!, Bob. name是位置参数,message是位置参数
# 不定长参数(位置参数和关键字参数)
def myFunction(*args, **kwargs):
for arg in args:
print(arg)
for key, value in kwargs.items():
print(f"{key} = {value}")
myFunction(1, 2, 3, name="Alice", age=25) # 1, 2, 3是位置参数;name="Alice", age=25是关键字参数
# 输出:
# 1
# 2
# 3
# name = Alice
# age = 25
# 函数作为参数
def func1():
print('调用了函数func1')
def func2(f):
f()
func2(func1)
# 输出: 调用了函数func1
拆包和装包
“拆包”(unpacking)和"装包"(packing)是处理序列(如列表、元组等)中元素的两种常用操作。这些操作在函数参数传递和返回值处理时特别有用。
- 拆包(Unpacking)
拆包是指将序列中的元素提取出来,作为独立的变量使用。在函数调用时,可以使用星号
*
来拆包序列,将其元素作为位置参数传递给函数。
list1 = [1, 2, 3]
print(*list1)
# 输出: 1 2 3
dict1 = {1: 'one', 2: 'two', 3: 'three'}
print(*dict1)
# 输出: one two three
tuple1 = (1, 2, 3)
print(*tuple1)
# 输出: 1 2 3
set1 = {1, 2, 3}
print(*set1)
# 输出: 1 2 3
list2 = ['Bob', 25]
info_1 = 'My name is {} and I am {} years old.'.format(*list2)
print(info_1) #--> My name is Bob and I am 25 years old.
dict2 = {'name': 'Charlie', 'age': 35}
info_2 = 'My name is {name} and I am {age} years old.'.format(**dict2)
print(info_2) #--> My name is Charlie and I am 35 years old.
- 拆包列表或元组作为函数参数
def greet(first_name, last_name): print(f"Hello, {first_name} {last_name}!") names = ['John', 'Doe'] greet(*names) # 输出:Hello, John Doe!
在这个例子中,names 列表被拆包成两个独立的参数 first_name 和 last_name,然后传递给 greet 函数。
字典拆包作为函数参数(Python 3.5+)
在 Python 3.5 及更高版本中,还可以使用双星号
**
来拆包字典,将其键值对作为关键字参数传递给函数。def print_info(name, age): print(f"Name: {name}, Age: {age}") info = {'name': 'Alice', 'age': 30} print_info(**info) # 输出:Name: Alice, Age: 30
- 装包(Packing)
装包是指将多个独立的变量组合成一个序列。在函数定义时,可以使用星号
*
来将位置参数装包成元组,或使用双星号**
来将关键字参数装包成字典。
- 装包位置参数
def sum_all(*numbers): return sum(numbers) print(sum_all(1, 2, 3, 4)) # 输出:10
在这个例子中,
sum_all
函数接受任意数量的位置参数,并将它们装包成numbers
元组。
- 装包关键字参数
def print_kwargs(**kwargs): for key, value in kwargs.items(): print(f"{key}: {value}") print_kwargs(city="New York", country="USA") # 输出: # city: New York # country: USA
在这个例子中,
print_kwargs
函数接受任意数量的关键字参数,并将它们装包成kwargs
字典。
综合示例
示例1:
def func(a, b=0,):
print(a)
print(b)
func(1) # a位置参数、b默认参数
# 输出:
# 1
# 0
func(1, 2) # a位置参数、b位置参数
# 输出:
# 1
# 2
func(a=1, b=2) # a关键字参数、b关键字参数
# 输出:
# 1
# 2
func(1, b=2) # 在没有不定长参数,指定位置参数赋值时,指定赋值必须在最后,a位置参数,b关键字参数
# 输出:
# 1
# 2
# func(a=1, 2) 报错,关键字参数不能放在前面
# func(2, a=1) 报错,关键字参数a多重赋值
示例2:
def func(a, b=0, *c):
print(a)
print(b)
print(c)
func(1, 2, 3, 4, 5, 6) # 均为位置参数
# 输出:
# 1
# 2
# (3, 4, 5, 6)
func(1) # 位置参数,默认参数
# 或func(a=1)
# 输出:
# 1
# 0
# ()
func(1, 2) # 均为位置参数
# 输出:
# 1
# 2
# ()
# func(a=1, 2, 3) 报错,关键字参数不能放在前面
# func(2, 3, a=1) 报错,关键字参数a多重赋值
# func(a=1, b=2, 3) 报错,关键字参数不能放在前面
#只要函数定义中出现不定长参数,则在使用关键字参数的时候就要慎重!
示例3:
# def func(*a, b): 报错,因为b无法拿到值,值全部都会给a
def func(*a, b=0): # 这样就没意义了
print(a)
print(b)
func(1, 2, 3)
# 输出:
# (1, 2, 3)
# 0
func(1, 2, 3, 4)
# 输出:
# (1, 2, 3, 4)
# 0
func(1, 2, 3, 4, b=5) # 可以通过关键字赋值给b
# 输出:
# (1, 2, 3, 4)
# 5
示例4:
def combine_and_print(*args, **kwargs):
# 装包位置参数成元组
print("Args:", args)
# 装包关键字参数成字典
print("Kwargs:", kwargs)
# 拆包元组和字典
first_arg, *rest_args = args
print("First arg:", first_arg)
print("Rest args:", rest_args)
for key, value in kwargs.items():
print(f"{key.upper()}: {value}")
# 调用函数并拆包列表和字典
data = [1, 2, 3]
meta = {'name': 'Bob', 'age': 25}
combine_and_print(*data, **meta)
# 输出:
# Args: (1, 2, 3)
# First arg: 1
# Rest args: [2, 3]
# NAME: Bob
# AGE: 25
在这个示例中,我们定义了一个函数 combine_and_print
,它接受任意数量的位置参数和关键字参数。然后,我们演示了如何在函数内部拆包和装包这些参数。
函数在内存中的形式
函数是对象,和其他对象一样,它们在内存中占据空间。当你定义一个函数时,Python 会为这个函数创建一个函数对象,并将其存储在内存中。这个函数对象包含了函数的代码、名称、以及其他一些元数据(如默认参数值、注解等)。
-
函数定义:
当你使用def
关键字定义一个函数时,Python 会创建一个函数对象,并将其赋值给函数名。例如:def my_function(): print("Hello, World!")
在这里,
my_function
是一个指向函数对象的引用。 -
函数对象的存储:
函数对象存储在 Python 的堆内存中(与栈内存相对,栈内存通常用于存储局部变量和函数调用帧)。堆内存是用于存储动态分配的对象(如列表、字典、函数等)的区域。 -
函数名和引用:
函数名(如my_function
)实际上是对函数对象的引用。你可以将这个函数对象赋值给其他变量,从而创建新的引用:another_reference = my_function another_reference() # 输出 "Hello, World!"
-
函数对象的属性:
函数对象有许多属性,如__name__
(函数名)、__code__
(编译后的字节码)、__defaults__
(默认参数值)、__globals__
(全局命名空间)等。这些属性存储在函数对象的内部结构中。 -
垃圾回收:
当没有引用指向一个函数对象时,该函数对象会成为垃圾回收的目标。Python 的垃圾回收机制会定期清理不再使用的对象,以释放内存。 -
闭包:
闭包是一种特殊的函数,它可以捕获并记住其外部作用域中的变量。这些变量也被存储在闭包对象的内部结构中,使得闭包可以在其外部作用域不再存在的情况下仍然访问这些变量。 -
调用函数:
当你调用一个函数时,Python 会在栈内存中创建一个新的帧(frame),用于存储局部变量、参数和其他与函数调用相关的信息。这个帧在函数调用结束时会被销毁。
函数传参中的传值和传址问题
学习python的第十三天之函数——函数传参中的传值和传址问题
匿名函数
匿名函数是指使用
lambda
关键字定义的简单函数。与普通的函数定义方式(使用def
关键字)不同,匿名函数通常在一行内完成定义,并且不需要显式地指定函数名。这种函数特别适合实现一些简单的、临时的功能,避免了为这些小功能定义完整函数的繁琐。语法:lambda 参数列表: 表达式
- 参数列表:可以包含多个参数,参数之间用逗号分隔。
- 表达式:只能有一个表达式,这个表达式的计算结果会被匿名函数返回。
两个数相加
add = lambda x, y: x + y print(add(3, 5)) # 输出 8
计算两个数的乘积
multiply = lambda x, y: x * y print(multiply(4, 5)) # 输出 20
判断一个数是否为偶数
is_even = lambda x: x % 2 == 0 print(is_even(4)) # 输出 True print(is_even(3)) # 输出 False
使用场景
匿名函数常用于需要函数对象的地方,比如:
作为函数参数:匿名函数可以直接作为其他函数的参数,特别是在需要传递简单函数逻辑时。
排序、过滤等操作中:在
sorted()
、filter()
、map()
等内置函数中,匿名函数常用于指定排序规则、过滤条件或映射逻辑。
在内置函数中使用匿名函数
- 排序
list1 = [('李四', 52, 168), ('张三', 18, 183), ('王五', 36, 175)] sorted_list1 = sorted(list1) print(sorted_list1) # 输出 [('张三', 18, 183), ('李四', 52, 168), ('王五', 36, 175)] # 按年龄排序,升序 sorted_list2 = sorted(list1, key=lambda x: x[1]) print(sorted_list2) # 输出 [('张三', 18, 183), ('王五', 36, 175), ('李四', 52, 168)] # 按身高排序,降序 sorted_list3 = sorted(list1, key=lambda x: x[2], reverse=True) print(sorted_list3) # 输出 [('张三', 18, 183), ('王五', 36, 175), ('李四', 52, 168)] # 按身高排序,降序(等效使用def定义函数) def sorted_list(a): x = a[2] return x sorted_list4 = sorted(list1, key=sorted_list, reverse=True) print(sorted_list4) # 输出 [('张三', 18, 183), ('王五', 36, 175), ('李四', 52, 168)]
- 过滤
even_numbers = filter(lambda x: x % 2 == 0, numbers) print(list(even_numbers)) # 输出 [2, 4]
- 映射
squares = map(lambda x: x ** 2, numbers) print(list(squares)) # 输出 [25, 4, 9, 1, 16]
注意事项
- 匿名函数虽然简洁,但不易于阅读和维护,特别是对于复杂的逻辑。
- 匿名函数没有函数名,因此不能直接通过函数名来调用它。通常,我们会将匿名函数赋值给一个变量,然后通过这个变量来调用它。
- 由于匿名函数只能包含一个表达式,因此它不适合实现复杂的逻辑。对于复杂的逻辑,还是应该使用
def
关键字来定义完整的函数。