提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
Python中的函数是组织好的、可重复使用的、用于实现单一或相关联功能的代码块。函数能提高应用的模块性,和代码的重复利用率
函数目的:
函数的目的是将一组执行特定任务的语句封装成一个独立的单元,以便在程序中多次重复使用,从而提高代码的可读性、可维护性和复用性。
函数定义:
函数定义是创建函数的过程,它指定了函数的名称、参数列表(如果有的话)以及函数体(即函数要执行的代码块)。
def my_function(param1, param2):
# 函数体
result = param1 + param2
return result
函数声明:
在某些编程语言中,函数声明可能指的是在函数实际实现之前提前通知编译器或解释器函数的存在及其签名(名称、参数类型和返回类型)。但在像Python这样的动态类型语言中,函数的“声明”和“定义”通常是同时进行的,因为类型是在运行时确定的。
def my_function(param1, param2):
"""
这个函数接收两个参数,并返回它们的和。
参数:
param1 -- 第一个加数
param2 -- 第二个加数
返回:
两个参数的和
"""
return param1 + param2```
# 函数调用:
函数调用是执行函数的过程。在函数调用时,程序的控制权转移到函数中,执行函数体内的代码,并在函数执行完毕后返回控制权给调用者。
```python
result = my_function(2, 3)
函数形参:
形参(形式参数)是函数定义中列出的参数,它们代表了函数将要接收的数据的占位符。在函数被调用时,形参会被赋予相应的实参值。
1. 位置参数(Positional Arguments)
最常见的形参类型,它们按照函数定义中的顺序接收传递给函数的实参。
def greet(name, age):
print(f"Hello, {name}. You are {age} years old.")
greet("Alice", 30) # 正确的调用方式
2. 默认值参数(Default Arguments)
为形参提供默认值,使得在调用函数时可以省略某些参数。如果调用时没有提供这些参数,则使用默认值。
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
greet("Bob") # 输出: Hello, Bob!
greet("Charlie", "Hi") # 输出: Hi, Charlie!
3. 可变参数(Varargs, *args)
允许函数接收任意数量的位置参数,这些参数被组织成一个元组。
def sum_numbers(*args):
total = 0
for num in args:
total += num
return total
print(sum_numbers(1, 2, 3, 4)) # 输出: 10
4. 关键字可变参数(Kwargs, **kwargs)
允许函数接收任意数量的关键字参数,这些参数被组织成一个字典。
def greet(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
greet(name="David", age=25) # 输出: name: David 和 age: 25
5. 命名关键字参数(Named Keyword Arguments)
Python 3中引入的特性,允许你强制函数调用者通过关键字而不是位置来传递某些参数。在命名关键字参数之前,可以使用*来分隔位置参数和命名关键字参数。
def greet(*, first_name, last_name):
print(f"Hello, {first_name} {last_name}!")
greet(first_name="John", last_name="Doe") # 正确
# greet("John", "Doe") # 这将引发TypeError,因为需要关键字参数
6. 混合参数类型
你可以在同一个函数中混合使用上述不同类型的形参,但需要注意它们的顺序:位置参数、默认值参数、*args
、命名关键字参数(之前的*)、**kwargs
。
def complex_function(a, b=2, *args, c=3, **kwargs):
print(f"a={a}, b={b}, args={args}, c={c}, kwargs={kwargs}")
# 注意:上面的定义在逻辑上可能不是最佳实践,因为c的位置可能令人困惑
# 通常,命名关键字参数(如果有的话)应该紧跟在*args之后
函数实参:
实参(实际参数)是在函数调用时传递给函数的参数值。这些值被赋值给函数的形参,以便在函数体内使用。
1.位置参数(Positional Arguments):
实参按照函数定义中参数的顺序和位置来传递。这是最常见的参数传递方式。
def greet(name, greeting):
print(f"{greeting}, {name}!")
greet("Alice", "Hello") # "Hello, Alice!"
2.关键字参数(Keyword Arguments):
实参通过参数名来传递,这允许实参以任意顺序传递,只要它们的名称与函数定义中的参数名相匹配。
greet(greeting="Hello", name="Bob") # "Hello, Bob!"
3.默认参数(Default Parameters):
在函数定义时,可以为参数指定默认值。调用函数时,如果没有为这些参数提供实参,则使用默认值。
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
greet("Charlie") # "Hello, Charlie!"
4.可变参数(Variable-length Arguments):
- 位置参数的可变数量:通过在参数名前加上*,可以将一个元组作为参数传递给函数,允许函数接收任意数量的位置参数。
def sum_numbers(*numbers):
return sum(numbers)
print(sum_numbers(1, 2, 3)) # 6
- 关键字参数的可变数量:通过在参数名前加上**,可以将一个字典作为参数传递给函数,允许函数接收任意数量的关键字参数。
def greet_with_info(**info):
for key, value in info.items():
print(f"{key}: {value}")
greet_with_info(name="David", age=30, job="Developer")
5.混合使用:
在函数定义或调用时,可以混合使用上述各种参数类型,但需要注意参数的顺序和类型匹配。
函数返回值:
函数可以通过return语句返回一个值给调用者。如果函数没有显式地返回任何值,则默认返回None(在某些语言中)。
1.单一返回值
大多数情况下,函数返回一个单一的值。你可以使用return语句后跟你想要返回的值来实现这一点。
def add(x, y):
return x + y
result = add(5, 3)
print(result) # 输出: 8
在这个例子中,add函数接受两个参数x和y,并返回它们的和。
2.多个返回值
虽然Python语法不支持直接返回多个值,但你可以利用元组(tuple)来模拟这种行为。当函数返回一个元组时,你可以通过解包(unpacking)这个元组来接收多个返回值。
def divide(x, y):
quotient = x // y
remainder = x % y
return quotient, remainder
quotient, remainder = divide(10, 3)
print(quotient, remainder) # 输出: 3 1
在这个例子中,divide函数执行整数除法,并返回商和余数。函数返回的是一个元组,然后我们使用元组解包来接收这两个值。
3.隐式返回None
如果函数中没有return语句,或者return语句后面没有跟任何值,那么函数将隐式地返回None。
def say_hello():
print("Hello, world!")
result = say_hello()
print(result) # 输出: None
在这个例子中,say_hello函数只打印了一条消息,并没有显式地返回任何值,因此它返回了None。
4.返回值类型
Python函数的返回值可以是任何数据类型,包括数字、字符串、列表、元组、字典、集合、对象,甚至是另一个函数(称为高阶函数)。返回值的类型完全取决于函数内部的逻辑和需要。
def get_person_info():
return {"name": "Alice", "age": 30, "city": "New York"}
info = get_person_info()
print(info) # 输出: {'name': 'Alice', 'age': 30, 'city': 'New York'}
在这个例子中,get_person_info函数返回了一个包含个人信息的字典。
函数的参数类型:
在Python中,函数的参数本身是不直接指定类型的,这与一些静态类型语言(如Java或C++)不同。Python是一种动态类型语言,这意味着变量的类型是在运行时动态确定的,而不是在编写代码时静态确定的。因此,当你定义一个函数时,你不需要(也不能)直接指定参数的类型。
然而,从Python 3.5开始,可以通过类型提示(Type Hints)来指定函数参数、返回值以及变量等的期望类型。这些类型提示是可选的,它们不会改变Python代码的运行方式,也不会强制执行类型检查,但它们可以被第三方工具(如类型检查器、IDE等)用来提供额外的类型信息,从而帮助开发者捕获类型相关的错误。
下面是一个使用类型提示的例子:
def greet(name: str, age: int) -> str:
return f"Hello, my name is {name} and I am {age} years old."
# 调用函数
result = greet("Alice", 30)
print(result) # 输出: Hello, my name is Alice and I am 30 years old.
# 注意:即使你传递了错误的类型(如下所示),Python 也不会抛出错误
# 这是因为类型提示是可选的,并且不会改变Python的动态类型系统
wrong_result = greet(30, "thirty") # 这将正常工作,但可能不是你想要的结果
print(wrong_result) # 输出可能是: Hello, my name is 30 and I am thirty years old.
在这个例子中,greet
函数接受两个参数:name
和age
,它们分别被注释为str
(字符串)和int
(整数)类型。函数还使用-> str
来指定它应该返回一个字符串类型的值。然而,如果你传递了错误类型的参数(如上例中的greet(30, "thirty")
),Python解释器不会抛出错误,因为类型提示是可选的,并且不会改变Python的动态类型特性。
要利用类型提示进行类型检查,你需要使用支持类型提示的第三方工具,如mypy
,这是一个流行的Python静态类型检查器。通过运行mypy
,你可以发现潜在的类型问题,从而帮助你在运行代码之前捕获错误。
匿名函数:
在Python中,匿名函数是通过lambda关键字来定义的。lambda函数本质上是一个小的匿名函数,它可以接受任何数量的参数,但只能有一个表达式。这个表达式的计算结果就是lambda函数的返回值。由于lambda函数是匿名的,所以它通常用于需要函数对象的场合,但又不想正式命名一个函数的场景。
lambda函数的一般语法是:
lambda arguments: expression
- arguments 是传递给函数的参数,可以是多个,用逗号隔开。
- expression 是一个关于参数的表达式,其计算结果就是函数的返回值。
1.单个参数
定义一个lambda函数,它接受一个参数并返回它的两倍
double = lambda x: x * 2
使用这个函数
print(double(5)) # 输出: 10
2.多个参数
定义一个lambda函数,它接受两个参数并返回它们的和
add = lambda x, y: x + y
# 使用这个函数
print(add(5, 3)) # 输出: 8
3.在高阶函数中使用
lambda函数经常与高阶函数(如filter(), map(), sorted(), reduce()等)一起使用,这些高阶函数接受一个或多个函数作为参数。
# 使用lambda函数与map()
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers))
print(squared) # 输出: [1, 4, 9, 16, 25]
# 使用lambda函数与filter()
filtered = list(filter(lambda x: x % 2 == 0, numbers))
print(filtered) # 输出: [2, 4]
# 使用lambda函数与sorted()
sorted_numbers = sorted(numbers, key=lambda x: x % 3)
print(sorted_numbers) # 输出可能因Python版本和解释器实现而异,但基本思路是按x除以3的余数排序
4.注意事项
- lambda函数主要用于编写简单的函数对象。对于更复杂的逻辑,应该使用标准的def语句来定义函数。
- lambda函数可以接收任意数量的参数,但只能有一个表达式。
- lambda函数在定义时不需要命名,但在使用时通常会被赋值给一个变量,这样你就可以通过该变量来引用它。
变量的作用与(global与nonlocal)
1.变量的作用
Python中变量的主要作用是存储数据,这些数据可以是基本数据类型(如整数、浮点数、字符串等),也可以是复杂的数据结构(如列表、字典等)。变量可以被赋值、修改和引用,是程序中非常重要的组成部分。通过变量,程序可以动态地存储和处理数据,使程序具有更强的灵活性和可扩展性。
2.global与nonlocal
global
:用于在函数内部声明一个变量为全局变量,以便在函数内部修改全局作用域中的变量。使用global
关键字时,不需要在引用全局变量时声明,但在修改全局变量时需要在函数内部进行声明。
a = 1 # 全局变量
def change():
global a # 声明a为全局变量
a += 1
print("函数内部的a的值:", a)
change()
print("调用change函数后, 函数外部的a的值:", a) # 输出 2
nonlocal
:用于在嵌套函数中声明一个变量为外层函数的局部变量,以便在嵌套函数内部修改外层函数的变量。注意,nonlocal
仅适用于Python 3,并且在Python 2中内嵌函数可以使用外层作用域中通过赋值的所有名称,但不能进行修改。
def demo_fun():
num = 0
def demo_fun_1():
nonlocal num # 声明num为外层函数的局部变量
num += 1
return num
return demo_fun_1()
print(demo_fun()) # 输出 1
递归函数
递归函数是一种在函数定义中调用自身的函数。它将一个大问题分解为一个或多个相似的子问题,并通过逐步解决这些子问题来解决原始问题。递归函数通常包含两个部分:基本情况和递归情况。
- 基本情况:递归函数停止调用自身的条件。
- 递归情况:函数调用自身的过程。
示例:计算阶乘的递归函数
def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n-1)
print(factorial(5)) # 输出 120
闭包
闭包是Python中的一种高级特性,它指的是在函数内部定义了另外一个函数,并返回了这个内部函数作为函数对象,同时还保存了外层函数的状态信息。这个内部函数可以依赖外层函数的变量和参数,而且外层函数返回的是这个内部函数的引用。
def outer_function(x):
def inner_function(y):
return x + y
return inner_function
f = outer_function(5)
print(f(3)) # 输出 8
装饰器
装饰器是Python中的一种高级特性,它允许在不修改原有函数或类代码的情况下,给函数或类增加新的功能。装饰器本质上是一个函数或类,它接收一个函数或类作为参数,并返回一个函数或类的修改版本。
示例:一个简单的装饰器,用于记录函数执行时间
import time
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"Function {func.__name__} took {end_time - start_time} seconds.")
return result
return wrapper
@timer
def my_function():
time.sleep(1) # 模拟耗时操作
print("Hello, world!")
my_function()
# 输出:
# Hello, world!
# Function my_function took 1.000969648361206 seconds.
总结
通过灵活使用这些参数类型,你可以编写出强大且灵活的Python函数。