Python(三)

本文最后更新于:2024年5月18日 下午

在Python中,函数是一种基本的数据结构,它允许你将一段代码封装起来,以便重复使用。下面是对你提到的各个概念的解释和例子:

1.不可变与可变参数

在Python中,可变参数(Mutable Arguments)和不可变参数(Immutable Arguments)是两种不同的参数类型,它们在函数内部的行为和用途上有所不同。

不可变参数

不可变参数指的是在函数内部不能被修改的对象,如整数、浮点数、字符串、元组等。一旦一个不可变对象被创建,其值就不能被更改。

示例
1
2
3
4
5
def func(a):
a = a + 1 # 尝试修改不可变对象
return a
num = 5
print(func(num)) # 输出: 5,因为num是不可变的

可变参数

可变参数指的是在函数内部可以被修改的对象,如列表(List)、字典(Dictionary)、集合(Set)等。这些对象在函数内部可以被添加、删除或修改元素。

示例
1
2
3
4
5
def func(lst):
lst.append(1) # 修改可变对象
return lst
my_list = [1, 2, 3]
print(func(my_list)) # 输出: [1, 2, 3, 1],因为my_list是可变的

在实际编程中,了解可变参数和不可变参数的区别非常重要,因为它们会影响函数的行为和结果。在处理不可变对象时,需要注意,因为尝试修改它们可能会导致错误。而对于可变对象,你可以自由地添加、删除或修改它们的内容。

2.位置参数

位置参数(Positional Arguments)是指在函数定义时按照参数位置顺序排列的参数。当你调用一个函数时,你需要按照函数定义中的参数顺序提供相应的值。

示例
1
2
3
4
5
6
def greet(name, message="Hello"):
"""打印一个问候语"""
print(f"{message}, {name}!")
# 调用函数,提供位置参数
greet("Alice") # 输出: Hello, Alice!
greet("Bob", "Hi there") # 输出: Hi there, Bob!

在这个例子中,greet 函数定义了两个位置参数:namemessage。在调用 greet 函数时,我们按照函数定义中的参数顺序提供值。如果没有提供第二个参数 message 的值,它将使用默认值 “Hello”。

位置参数的特点

  1. 必须按照顺序提供:调用函数时,必须按照函数定义中的参数顺序提供值。
  2. 参数名称不重要:位置参数的名称在调用时不需要指定,Python会根据位置来匹配参数。
  3. 不能有默认值:位置参数不能有默认值,即在函数定义时不能指定默认值。
  4. 可以有多个:一个函数可以有多个位置参数,但必须按照定义的顺序提供。
    位置参数是函数定义中最常见的一种参数类型,它允许你直接通过位置来传递参数,使得函数调用更加直观和易于理解。

3.关键字参数

函数的关键字参数允许你通过参数名而不是位置来指定参数值。这意味着你可以在调用函数时以任意顺序提供参数,只要指定了参数名。关键字参数是在函数定义中使用 = 赋值的参数。

示例

展示了如何定义和使用关键字参数:

1
2
3
4
5
6
7
def greet(name, message="Hello"):
"""打印一个问候语"""
print(f"{message}, {name}!")
# 使用关键字参数调用函数
greet(name="Alice", message="Hi")
greet(message="Hi", name="Alice")
greet(name="Bob")

在这个例子中,greet 函数接受两个参数:namemessagemessage 参数有一个默认值 “Hello”。在调用 greet 函数时,我们可以通过指定参数名来提供值,这样可以不依赖于参数的顺序,并且可以在没有提供值时使用参数的默认值。

适用情况

  1. 当函数有多个参数且其中一些参数有默认值时,使用关键字参数可以让代码更清晰,因为它们明确指出了每个参数的含义。
  2. 当你只想更新函数的某些参数而不是所有参数时,关键字参数非常有用。
  3. 当函数参数列表很长时,使用关键字参数可以避免记住参数的顺序,从而减少错误。
    关键字参数必须在位置参数之后提供,否则会引发语法错误。例如,你不能在位置参数之前使用关键字参数:
    1
    greet(name="Alice", "Hi")  # 这会引发语法错误
    正确的方式是:
    1
    greet("Alice", message="Hi")  # 这是正确的

4.缺省参数

缺省参数(default parameter)是指在没有传入值的情况下,函数会使用默认值的参数。当你定义一个函数时,可以为参数设置默认值,这样在调用函数时如果不提供该参数的值,函数会使用默认值。

示例
1
2
3
4
5
6
def greet(name, message="Hello"):
"""打印一个问候语"""
print(f"{message}, {name}!")
# 使用缺省参数调用函数
greet("Alice") # 使用了message的默认值"Hello"
greet("Bob", "Hi there") # 覆盖了message的默认值

在这个例子中,greet 函数有两个参数:namemessagemessage 参数有一个默认值 “Hello”。当我们调用 greet 函数时,如果没有为 message 提供值,它将自动使用默认值 “Hello”。
在使用缺省参数时,需要注意以下几点:

  1. 缺省参数通常应该放在参数列表的最后,这样可以避免在调用函数时出现不必要的混淆。
  2. 缺省参数的值在函数定义时只会计算一次,这意味着如果缺省参数是一个可变对象(如列表、字典或实例),那么在函数多次调用之间,这个对象可能会保留状态,这可能会导致意外的行为。
例如:
1
2
3
4
5
6
7
def append_to(element, to=[]):
to.append(element)
return to
my_list = append_to(12)
print(my_list) # 输出: [12]
another_list = append_to(42)
print(another_list) # 输出: [12, 42],这不是预期的行为

为了避免这种问题,可以使用 None 作为缺省参数的默认值,然后在函数内部进行检查并分配一个新的对象:

1
2
3
4
5
6
7
8
9
def append_to(element, to=None):
if to is None:
to = []
to.append(element)
return to
my_list = append_to(12)
print(my_list) # 输出: [12]
another_list = append_to(42)
print(another_list) # 输出: [42],这是预期的行为

这样,每次调用 append_to 函数时,如果没有提供 to 参数,它都会创建一个新的列表,从而避免了在不同调用之间共享状态的问题。

5.不定长参数

不定长参数(Var-Length Arguments)指的是函数可以接受任意数量的非关键字参数。这通常通过在函数定义中使用星号 * 来指定。
不定长参数可以分为两种:

可变数量的位置参数

使用星号 * 跟随一个参数名,表示该参数可以接受任意数量的位置参数。这些参数会被打包成一个元组(tuple)。

1
2
3
def print_info(*vartuple):
print("vartuple:", vartuple)
print_info(10, 20, 30) # 输出: vartuple: (10, 20, 30)

可变关键字参数

使用双星号 ** 跟随一个参数名,表示该参数可以接受任意数量的关键字参数。这些参数会被打包成一个字典(dictionary)。

1
2
3
def print_info(**varkwargs):
print("varkwargs:", varkwargs)
print_info(name="Alice", age=25) # 输出: varkwargs: {'name': 'Alice', 'age': 25}

可变数量的位置参数和可变关键字参数可以同时使用,以允许函数接受任意数量的非关键字参数和任意数量的关键字参数。

1
2
3
4
5
6
def print_info(*vartuple, **varkwargs):
print("vartuple:", vartuple)
print("varkwargs:", varkwargs)
print_info(10, 20, 30, name="Alice", age=25) # 输出:
# vartuple: (10, 20, 30)
# varkwargs: {'name': 'Alice', 'age': 25}

使用不定长参数时

注意
  • 不定长参数必须在函数定义中的其他位置参数之后。
  • 不定长参数在函数调用时可以不提供值,也可以提供任意数量的值。
  • 不定长参数的值会被收集到一个元组或字典中,而不是直接传递给函数的参数。
  • 如果你需要访问不定长参数中的值,你可以使用 *vartuple**varkwargs 来解包这些值。

6.匿名函数

匿名函数(Anonymous Functions)也被称为Lambda函数,它们是没有名称的函数,通常只包含一个表达式。Lambda函数的语法如下:

1
lambda arguments: expression
  • arguments:这是传递给Lambda函数的参数,可以是一个参数,也可以是多个参数,参数之间用逗号分隔。
  • expression:这是Lambda函数执行的表达式,它的结果就是Lambda函数的返回值。
    Lambda函数通常用于需要快速定义一个简单的、一次性的函数,或者用于Python的某些内置函数,如 map()filter()sorted()
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 计算两个数的和
add = lambda x, y: x + y
print(add(5, 3)) # 输出: 8
# 检查一个数字是否为偶数
is_even = lambda x: x % 2 == 0
print(is_even(4)) # 输出: True
print(is_even(5)) # 输出: False
# 使用Lambda函数作为map()的函数对象
numbers = [1, 2, 3, 4, 5]
squared_numbers = map(lambda x: x * x, numbers)
print(list(squared_numbers)) # 输出: [1, 4, 9, 16, 25]
# 使用Lambda函数作为filter()的函数对象
numbers = [1, 2, 3, 4, 5]
even_numbers = filter(lambda x: x % 2 == 0, numbers)
print(list(even_numbers)) # 输出: [2, 4]

Lambda函数的特点

  • 不能包含代码块,只能包含一个表达式。
  • 不能有参数列表外的代码。
  • 不能包含任意复杂的逻辑,因为它们只能包含一个表达式。
  • 不能有返回语句,因为表达式的结果就是返回值。

Lambda函数通常用于定义简单的、一次性的函数,或者作为其他函数的参数。它们在处理简单任务时非常有用,但如果需要编写更复杂的函数,应该使用普通的函数定义。

7.函数装饰器

函数装饰器(Function Decorator)是Python中一个强大的特性,它允许你在不修改函数代码的情况下,给函数添加额外的功能。装饰器本质上是一个返回函数的高阶函数。
在Python中,装饰器通常以@符号开头,后面跟着装饰器的名称。当一个函数被一个装饰器装饰时,实际上装饰器返回了一个新的函数,这个新的函数会替换原始函数。

简单示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello() # 输出:
# Something is happening before the function is called.
# Hello!
# Something is happening after the function is called.

在这个例子中,my_decorator是一个装饰器,它接收一个函数作为参数。wrapper是一个新的函数,它包含原始函数say_hello以及额外的打印语句。当调用say_hello时,实际上调用的是wrapper函数。
装饰器可以接收参数,这样就可以根据不同的情况应用不同的装饰器。

例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def repeat(num_times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
print("Repeating...")
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def say_hello():
print("Hello!")
say_hello() # 输出:
# Repeating...
# Hello!
# Repeating...
# Hello!
# Repeating...
# Hello!

在这个例子中,repeat是一个装饰器工厂,它接受一个参数num_times。当repeat(3)被应用到say_hello函数上时,它会创建一个新的装饰器,这个装饰器会重复调用say_hello三次,并在每次调用前后打印重复的提示。

装饰器在Python中非常常用,它们可以用于日志记录、性能测试、权限检查等场景。

8.模块的定义与使用

模块(Module)是一个包含Python代码的文件,它用于组织代码、重用代码和隐藏实现细节。模块通常包含函数、类、变量和文档字符串。

模块的定义与使用主要包括以下几个步骤:

  1. 创建模块
    创建一个包含Python代码的文件,该文件名通常是模块名,扩展名为.py
  2. 编写模块代码
    在模块文件中,你可以编写函数、类、变量和文档字符串。
  3. 使用模块
    要使用模块,你需要导入它。导入模块有几种方式,包括导入整个模块、导入模块中的特定函数或类、使用 from ... import ... 语法等。
    下面是一个简单的模块示例,以及如何使用它:
    模块文件(module.py)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # module.py
    # 定义一个函数
    def greet(name):
    return f"Hello, {name}!"
    # 定义一个类
    class Person:
    def __init__(self, name, age):
    self.name = name
    self.age = age
    def introduce(self):
    return f"My name is {self.name} and I am {self.age} years old."
    使用模块的Python文件
    1
    2
    3
    4
    5
    6
    7
    8
    # main.py
    # 导入模块
    import module
    # 使用模块中的函数
    print(module.greet("Alice")) # 输出: Hello, Alice!
    # 创建模块中的类实例
    person = module.Person("Bob", 30)
    print(person.introduce()) # 输出: My name is Bob and I am 30 years old.
    在这个例子中,module.py 是一个模块文件,其中包含了一个函数 greet 和一个类 Person。在 main.py 文件中,我们使用 import module 语句来导入整个模块。然后,我们使用 module.greet("Alice") 来调用模块中的函数,并使用 module.Person("Bob", 30) 来创建模块中的类实例。

模块的使用可以提高代码的可维护性和可读性,因为它们允许将相关的代码组织在一起,并且可以在不同的项目中重用这些代码。


Python(三)
https://forever0823.github.io/2024/05/04/函数/
作者
Alan
发布于
2024年5月4日
许可协议