python入门(7)函数系列 2

本文详细介绍了Python中的作用域规则,包括全局作用域和局部作用域,以及命名空间的层次结构。此外,还探讨了文档字符串在函数、类和模块中的重要性,以及如何编写和使用它们。参数解包的概念和用法也在文中得到阐述,包括函数调用时的参数解包和赋值语句中的参数解包。最后,文章提到了Python中的一些高级概念,如装饰器和闭包,以及它们在实际编程中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 作用域

在Python中,作用域是指变量在程序中可访问的范围。

1.1作用域种类

Python中有以下几种作用域:

(1)全局作用域(Global Scope):全局作用域是在整个程序中都可访问的作用域。在全局作用域中定义的变量可以在程序的任何地方被访问。

(2)局部作用域(Local Scope):局部作用域是在函数内部或代码块内部定义的作用域。在局部作用域中定义的变量只能在其所属的函数或代码块内部被访问。

1.2 作用域规则

Python中的作用域规则如下:

  • 在函数内部,可以访问函数内部定义的变量、函数的参数以及全局作用域中定义的变量。

  • 在函数内部,如果有同名的变量,优先使用局部作用域中的变量。如果局部作用域中没有该变量,才会去全局作用域中查找。

  • 在函数外部,可以访问全局作用域中定义的变量。

下面是一个示例代码,演示了全局作用域和局部作用域的使用:

x = 10  # 全局作用域中的变量

def foo():
    y = 20  # 局部作用域中的变量
    print(x)  # 访问全局作用域中的变量
    print(y)  # 访问局部作用域中的变量

foo()
print(x)  # 在函数外部访问全局作用域中的变量

输出结果为:

10
20
10

在上述示例中,变量x是全局作用域中定义的,在函数内部和外部都可以访问。变量y是函数foo内部的局部作用域中定义的,只能在函数内部被访问。

2. 命名空间

2.1 定义

在Python中,命名空间(Namespace)是一个用于存储和查找变量名的容器。它提供了变量名与变量对象之间的映射关系。

2.2 命名空间的类型

Python中的命名空间可以分为以下几种类型:

(1)内置命名空间(Built-in Namespace):它包含了Python解释器提供的内置函数和内置对象的名称。这些内置函数和对象在Python解释器启动时就被加载到内存中的内置命名空间中,可以直接使用,无需导入任何模块。

(2)全局命名空间(Global Namespace):它是在程序的顶层定义的命名空间,包含了在全局作用域中定义的变量、函数和类等。全局命名空间对于整个程序是可见的,可以在程序的任何地方访问其中的变量和函数。

(3)局部命名空间(Local Namespace):它是在函数或代码块内部定义的命名空间,包含了在局部作用域中定义的变量、函数和类等。局部命名空间只在其所属的函数或代码块内部有效,外部无法访问其中的变量和函数。

在Python中,命名空间之间存在着层级关系,即嵌套关系。内部命名空间可以访问外部命名空间中的变量,而外部命名空间不能直接访问内部命名空间中的变量。

2.3 查找规则

命名空间的查找规则如下:

(1)当访问一个变量时,Python首先在当前命名空间中查找,如果找到则使用该变量。如果当前命名空间中没有该变量,则继续向上一级命名空间查找,直到找到该变量或达到全局命名空间。

(2)如果在全局命名空间中仍然找不到该变量,则会引发NameError异常。

下面是一个示例代码,演示了命名空间的使用:

x = 10  # 全局命名空间中的变量

def foo():
    y = 20  # 局部命名空间中的变量
    print(x)  # 访问全局命名空间中的变量
    print(y)  # 访问局部命名空间中的变量

foo()

输出结果为:

10
20

在上述示例中,函数foo中的局部命名空间可以访问外部的全局命名空间中的变量x,但全局命名空间无法直接访问函数foo中的局部命名空间中的变量y

3. 文档字符串

在Python中,文档字符串(Docstring)是一种用于注释函数、类、模块等对象的字符串。它提供了对这些对象进行说明、描述和文档化的功能。文档字符串通常位于对象的定义之后,使用多行字符串的形式表示。

文档字符串在Python中具有特殊的含义,可以通过内置函数help()来获取对象的帮助信息。文档字符串的编写规范是符合PEP 257文档,其中包括了以下几个重要的约定:

  • 文档字符串应该放在对象的定义之后,并且使用一对三引号(''')或三个双引号(""")括起来。

  • 文档字符串的第一行应该是对象的简要摘要,使用大写字母开头,并以句号结尾。

  • 如果文档字符串包含多个段落,则段落之间应该用空行分隔。

  • 在文档字符串中,可以包含对对象的使用方法、参数说明、返回值、异常处理等详细的描述。

  • 可以使用标记语法(reStructuredText或Markdown)来格式化文档字符串,以增强可读性。

下面是一个示例代码,演示了如何编写文档字符串

def greet(name):
    """
    返回一个包含问候语的字符串。

    参数:
    name -- 要问候的人的名字

    返回值:
    包含问候语的字符串

    示例:
    >>> greet('Alice')
    'Hello, Alice! How are you?'
    """
    return f'Hello, {name}! How are you?'

在上述示例中,函数greet的文档字符串提供了关于函数的说明、参数说明和示例使用的信息。使用help(greet)greet.__doc__可以获取函数greet的文档字符串,从而了解函数的用途和使用方法。

文档字符串的编写是一种良好的编程习惯,它可以提供代码的可读性和可维护性,使其他开发者更容易理解和使用你的代码。在编写函数、类、模块等对象时,建议养成编写清晰、详细的文档字符串的习惯。

4. 参数解包

4.1 定义

在Python中,参数解包(Unpacking)是指将可迭代对象(如列表、元组、集合等)中的元素解包并分配给函数或变量。参数解包可以用于函数调用时传递参数,或者在赋值语句中将可迭代对象的元素赋值给多个变量。

参数解包的语法是使用*操作符,它可以应用在函数调用时和赋值语句中,具体取决于使用的上下文。

4.2 函数调用时的参数解包

函数调用时的参数解包:

当调用函数时,可以使用参数解包将可迭代对象的元素作为函数的参数传递,每个元素对应一个参数。参数解包使用*操作符来标识。

示例代码:

numbers = [1, 2, 3, 4, 5]
print(*numbers)  # 等同于 print(1, 2, 3, 4, 5)

# 函数定义
def add(a, b, c):
    return a + b + c

result = add(*numbers)  # 等同于 add(1, 2, 3)
print(result)  # 输出 6

在上述示例中,print(*numbers)将列表numbers的元素解包为单独的参数传递给print函数。同样地,add(*numbers)将列表numbers的元素解包为单独的参数传递给add函数。

4.3 赋值语句中的参数解包

参数解包也可以用于赋值语句,将可迭代对象的元素解包并分配给多个变量。

示例代码:

numbers = [1, 2, 3]
a, b, c = numbers  # 解包赋值
print(a, b, c)  # 输出 1 2 3

# 交换两个变量的值
x = 10
y = 20
x, y = y, x  # 解包赋值
print(x, y)  # 输出 20 10

在上述示例中,a, b, c = numbers将列表numbers的元素解包并分配给变量abc。另外一个例子是交换变量xy的值,通过解包赋值x, y = y, x实现了交换。

参数解包在以下情况下常用:

  • 函数需要接受可变数量的参数时,可以使用参数解包将可迭代对象的元素作为函数的参数传递。
  • 在处理多个返回值的函数调用时,可以使用参数解包将返回的多个值分配给不同的变量。
  • 在交换变量的值、迭代可迭代对象的元素等场景下,使用参数解包可以简化代码。

需要注意的是,参数解包时可迭代对象的元素个数必须与接收参数的变量个数相匹配,否则会引发ValueError异常。如果可迭代对象的长度超过接收参数的变量个数,则多余的元素会被忽略;如果可迭代对象的长度少于接收参数的变量个数,则会引发异常。

示例代码:

# 参数解包时可迭代对象的元素个数与变量个数匹配
numbers = [1, 2, 3]
a, b, c = numbers  # 解包赋值
print(a, b, c)  # 输出 1 2 3

# 参数解包时可迭代对象的元素个数多于变量个数
numbers = [1, 2, 3, 4, 5]
a, b, c = numbers  # 引发 ValueError: too many values to unpack

# 参数解包时可迭代对象的元素个数少于变量个数
numbers = [1, 2]
a, b, c = numbers  # 引发 ValueError: not enough values to unpack

需要注意的是,在参数解包时,可迭代对象可以是任何支持迭代的对象,例如列表、元组、集合、字符串等。

5. 返回值

在 Python 中,函数可以通过 return 语句来返回一个值。return 语句用于指定函数执行完成后要返回的结果,将该值传递给调用该函数的地方。

函数可以返回任意类型的值,包括数字、字符串、列表、字典、元组等。如果函数没有显式地使用 return 语句返回值,或者使用 return 语句但没有指定返回值,则函数默认返回 None

示例代码:

# 返回单个值
def add_numbers(a, b):
    return a + b

result = add_numbers(2, 3)
print(result)  # 输出 5

# 返回多个值
def get_name_and_age():
    name = "John"
    age = 25
    return name, age

name, age = get_name_and_age()
print(name)  # 输出 "John"
print(age)   # 输出 25

# 函数默认返回 None
def say_hello():
    print("Hello")

result = say_hello()
print(result)  # 输出 None

需要注意的是,函数执行到 return 语句后,将立即退出函数,并返回指定的值。因此,return 语句后面的代码将不会被执行。如果函数没有 return 语句或者 return 后面没有指定返回值,则函数执行完最后一行代码后将自动返回 None

6. 高阶函数

在Python中,高阶函数是指能够接受其他函数作为参数或者返回函数作为结果的函数。

6.1 高阶函数有哪些

Python提供了多个内置的高阶函数,常用的高阶函数包括:

(1)map(function, iterable): 将函数应用于可迭代对象的每个元素,并返回一个迭代器,包含应用函数后的结果。

numbers = [1, 2, 3, 4, 5]

def square(x):
    return x ** 2

squared_numbers = map(square, numbers)
print(list(squared_numbers))  # 输出 [1, 4, 9, 16, 25]

(2)filter(function, iterable): 使用函数过滤可迭代对象中的元素,只保留满足条件的元素,并返回一个迭代器。

numbers = [1, 2, 3, 4, 5]

def is_even(x):
    return x % 2 == 0

even_numbers = filter(is_even, numbers)
print(list(even_numbers))  # 输出 [2, 4]

(3)reduce(function, iterable[, initializer]): 使用指定的函数将可迭代对象中的元素累积计算,并返回最终结果。

from functools import reduce

numbers = [1, 2, 3, 4, 5]

def multiply(x, y):
    return x * y

product = reduce(multiply, numbers)
print(product)  # 输出 120

(4)sorted(iterable[, key][, reverse]): 对可迭代对象进行排序,并返回一个新的已排序的列表。

numbers = [5, 2, 4, 1, 3]

sorted_numbers = sorted(numbers)
print(sorted_numbers)  # 输出 [1, 2, 3, 4, 5]

(5)zip(*iterables): 将多个可迭代对象中对应位置的元素打包成元组,并返回一个迭代器。

names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]

zipped_data = zip(names, ages)
print(list(zipped_data))  # 输出 [('Alice', 25), ('Bob', 30), ('Charlie', 35)]

这些高阶函数可以极大地简化代码,并且提高代码的可读性和灵活性。它们常用于函数式编程、数据处理和迭代操作等场景。

6.2 高阶函数与内置函数的区别

内置函数并不完全等同于高阶函数,尽管某些内置函数也可以被认为是高阶函数。内置函数是Python语言提供的一组预定义的函数,可以直接使用,而不需要进行额外的导入或定义。这些内置函数可以接受零个或多个参数,并返回相应的结果。

高阶函数是一种特殊的函数类型,它可以接受其他函数作为参数或者返回函数作为结果。高阶函数的特点是将函数视为第一类对象,可以像操作其他数据类型一样操作函数。

尽管在Python的内置函数中有一些是高阶函数,例如map()filter()sorted()等,但并不是所有的内置函数都是高阶函数。内置函数包括各种类型的函数,如数学函数、字符串处理函数、类型转换函数等,并不限于只有高阶函数。

因此,内置函数和高阶函数虽然有重叠的部分,但并不完全等同。内置函数是Python提供的预定义函数集合,而高阶函数是一种特殊的函数类型,具备接受和返回函数的能力。

7. 解包

在Python中,闭包(Closure)是指一个函数对象与其引用的外部变量(非全局变量)组合而成的实体。它可以访问定义在其外部作用域的变量,并将其保存在函数内部,即使在外部作用域不可访问时,闭包仍然可以使用这些变量。

闭包通常是在一个函数内部定义了另一个函数,并且内部函数引用了外部函数的变量。当外部函数执行完成后,它的内部函数仍然可以访问和操作外部函数中的变量。

闭包的主要特点是:

  1. 内部函数引用了外部函数的变量。
  2. 外部函数返回内部函数。
  3. 内部函数可以在外部函数执行完成后继续访问和操作外部函数的变量。

闭包的使用场景包括但不限于:

  1. 封装数据:使用闭包可以创建一个私有的数据空间,外部无法直接访问和修改内部函数引用的变量,可以实现数据的封装和保护。
  2. 保留状态:闭包可以在内部函数中保留某个变量的状态,使得每次调用内部函数时可以记住上一次的状态。
  3. 延迟执行:闭包可以将某些操作延迟执行,以便在需要的时候调用。

以下是一个简单的闭包示例代码:

def outer_function(x):
    def inner_function(y):
        return x + y
    return inner_function

add_five = outer_function(5)  # 创建闭包,传入参数5,返回inner_function
result = add_five(3)  # 调用闭包,传入参数3,返回结果8
print(result)  # 输出:8

在上面的例子中,outer_function 是外部函数,它接收一个参数 xinner_function 是内部函数,它引用了外部函数的变量 x。通过调用 outer_function(5),我们创建了一个闭包 add_five,它可以在调用时将传入的参数与外部函数的变量相加。

闭包的特点是,即使 outer_function 执行完成后,add_five 仍然可以访问和使用 x 的值。这使得闭包能够保留外部函数的状态,并在需要时进行调用。

8. 装饰器

8.1 定义

装饰器(Decorator)是Python中一种用于修改函数或类的行为的特殊函数。装饰器允许我们在不修改原始函数代码的情况下,通过在函数定义前使用装饰器函数来对函数进行扩展、修改或包装。

装饰器的基本概念是,它接收一个函数作为输入,并返回一个新的函数作为输出。这个新函数可以在调用原始函数之前或之后执行额外的操作,或者完全替换原始函数的行为。

装饰器的语法使用了 @ 符号,将装饰器函数应用于目标函数。装饰器函数可以定义在目标函数之前或之后的任意位置。

8.2 装饰器的引入和使用

以下是一个简单的装饰器示例代码:

def decorator_function(func):
    def wrapper():
        print("Before function execution")
        func()
        print("After function execution")
    return wrapper

@decorator_function
def hello():
    print("Hello, world!")

hello()

在上面的例子中,我们定义了一个装饰器函数 decorator_function,它接收一个函数作为参数 func。在装饰器函数内部,我们定义了一个内部函数 wrapper,它在调用原始函数之前和之后打印额外的信息。最后,装饰器函数返回内部函数 wrapper

通过在目标函数 hello 的定义前使用 @decorator_function,我们将装饰器应用于 hello 函数。当我们调用 hello() 时,实际上是调用了被装饰后的函数 wrapper,它在执行原始函数之前和之后打印了额外的信息。

装饰器的使用场景包括但不限于:

  • 添加日志记录:可以在函数执行前后记录日志。
  • 计时函数执行时间:可以在函数执行前后计时,并打印执行时间。
  • 缓存函数结果:可以缓存函数的返回结果,避免重复计算。
  • 验证权限:可以在函数执行前检查用户权限。
  • 修改函数行为:可以修改函数的输入、输出或逻辑,增强函数功能。

需要注意的是,装饰器在定义时会立即执行,并且只执行一次。因此,装饰器通常是在导入模块时进行装饰操作。如果想要带参数的装饰器,可以使用装饰器工厂函数的方式来定义。

通过定义适合自己需求的装饰器,可以使代码更加灵活和可扩展。同时,Python 中还提供了一些内置的装饰器,如 @property@staticmethod@classmethod 等,用于特定的功能和用法。

总而言之,装饰器是 Python 中强大且灵活的特性,可以帮助我们在不修改原始函数代码的情况下扩展和定制函数的行为。

8.3 装饰器的分类

8.3.1 简单装饰器:

def decorator_function(func):
    def wrapper():
        print("Before function execution")
        func()
        print("After function execution")
    return wrapper

@decorator_function
def hello():
    print("Hello, world!")

hello()

在示例中,我们定义了一个名为decorator_function的装饰器函数,它接受一个函数作为参数。

装饰器函数内部定义了一个名为wrapper的内部函数,该函数在被修饰函数执行之前和之后分别输出一些信息。

最后,使用@decorator_function将装饰器应用到hello函数上,相当于将hello函数作为参数传递给decorator_function函数,然后将返回的wrapper函数绑定到hello函数的名称上,从而完成了装饰。

当调用hello()时,实际上是调用了经过装饰器修饰后的wrapper函数。这样,在执行hello函数之前,会先打印"Before function execution",然后执行hello函数本身,打印"Hello, world!",最后打印"After function execution"。

8.3.2 带参数的装饰器:

(1)说明:

在装饰器函数内部,可以访问外部装饰器函数的参数arg1arg2。这意味着我们可以在装饰器中使用这些参数来执行特定的操作。

(2)示例与分析

def decorator_function_with_args(arg1, arg2):
    def decorator_function(func):
        def wrapper(*args, **kwargs):
            print("Decorator arguments:", arg1, arg2)
            print("Before function execution")
            func(*args, **kwargs)
            print("After function execution")
        return wrapper
    return decorator_function

@decorator_function_with_args("Arg1", "Arg2")
def hello(name):
    print("Hello, ", name)

hello("Alice")

首先,我们定义了一个装饰器函数decorator_function_with_args,它接受两个参数arg1arg2。这个装饰器函数返回另一个装饰器函数decorator_function

然后,我们使用装饰器语法将hello函数装饰起来:@decorator_function_with_args("Arg1", "Arg2")。这相当于将hello函数作为参数传递给装饰器函数decorator_function_with_args,并将其返回的装饰器函数应用于hello函数。

当我们调用hello("Alice")时,以下步骤会依次发生:

  1. decorator_function_with_args("Arg1", "Arg2")被调用,返回一个装饰器函数decorator_function
  2. decorator_function(hello)被调用,返回一个内部函数wrapper
  3. wrapper("Alice")被调用。
  4. wrapper函数内部,它首先打印装饰器函数的参数arg1arg2,即"Decorator arguments: Arg1 Arg2"
  5. 然后,它打印"Before function execution",表示函数执行前的操作。
  6. 接下来,它调用被装饰的函数hello,并将参数"Alice"传递给它。
  7. hello函数打印"Hello, Alice"
  8. 最后,wrapper函数打印"After function execution",表示函数执行后的操作。

这样,装饰器函数实现了在函数执行前后添加额外的操作,同时可以通过装饰器函数的参数来定制这些操作。

在这个示例中,我们可以看到装饰器函数decorator_function_with_args的参数arg1arg2在装饰器的执行过程中被传递并打印出来。这意味着我们可以根据不同的参数定制装饰器的行为。

8.3.3 类装饰器:

(1)定义

类装饰器是通过定义一个类来实现装饰器的功能。

类装饰器的关键在于实现__call__方法,该方法使得类的实例可以像函数一样被调用。在__call__方法内部,我们可以定义在被装饰函数执行前后需要执行的操作。

(2)示例与分析

class DecoratorClass:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print("Before function execution")
        self.func(*args, **kwargs)
        print("After function execution")

@DecoratorClass
def hello():
    print("Hello, world!")

hello()

在示例中,我们定义了一个类装饰器DecoratorClass,它接受一个函数作为参数并保存在实例的func属性中。然后,我们实现了__call__方法,在该方法内部执行了在函数执行前后需要执行的操作。

接下来,我们使用@DecoratorClasshello函数装饰起来。当我们调用hello函数时,实际上是调用了DecoratorClass的实例,因为它已经被转换为可调用对象。在调用过程中,类装饰器的__call__方法被触发,从而执行了装饰器定义的操作。

最后,我们可以看到输出结果如下:

Before function execution
Hello, world!
After function execution

通过使用类装饰器,我们可以在不修改原始函数定义的情况下,为函数添加额外的功能。这种方式更加灵活,可以实现更复杂的装饰器逻辑,例如带有状态的装饰器。

8.3.4 内置装饰器:

(1)@property

  • @property可以将类中的方法转换为属性,使得我们可以像访问属性一样访问这个方法。
class Circle:
    def __init__(self, radius):
        self.radius = radius

    @property
    def area(self):
        return 3.14 * self.radius * self.radius

circle = Circle(5)
print(circle.area)  # Output: 78.5

在示例中,我们定义了一个名为Circle的类,它具有一个构造函数__init__和一个被@property装饰的area方法。

__init__方法用于初始化圆的半径,而area方法则用于计算圆的面积。

area方法上方添加@property装饰器后,它被转换为一个可访问的属性,而不是一个方法。这意味着我们可以像访问属性一样使用circle.area来获取圆的面积,而无需使用括号调用它。

最后,我们可以看到输出结果为78.5,表示圆的面积。

使用@property装饰器可以方便地将方法封装为属性,使得代码更加简洁和易读。同时,它还提供了对属性的访问控制和计算逻辑的灵活性。

(2)@staticmethod

  • @staticmethod:将方法转换为静态方法,无需访问实例属性。

静态方法是与类相关联的方法,但不需要访问类的实例或类的任何属性。它们是类的一部分,但在调用时与类的实例无关。静态方法在类的命名空间中定义,并使用@staticmethod装饰器标记。

class MathUtils:
    @staticmethod
    def add(x, y):
        return x + y

print(MathUtils.add(2, 3))  # Output: 5

在上述示例中,MathUtils类定义了一个静态方法add,该方法接受两个参数 xy,并返回它们的和。由于该方法是静态方法,我们可以直接通过类名调用它,而不需要创建类的实例。

在主程序中,我们使用MathUtils.add(2, 3)调用了静态方法add,传递参数2和3。执行结果为5,即两个参数的和。

静态方法适用于那些不依赖于实例状态,仅依赖于传入参数的操作。它们可以用于实现通用的功能或实用函数,与特定实例无关。

请注意,静态方法不能访问类的实例属性或调用其他实例方法,因为它们不与特定实例关联。它们主要用于实现独立于实例的逻辑。

(3)@classmethod

  • @classmethod:将方法转换为类方法,可以访问类属性。

类方法是与类相关联的方法,它可以访问类的属性,并且可以通过类名直接调用,而不需要实例化类。类方法在类的命名空间中定义,并使用@classmethod装饰器标记。

class Circle:
    pi = 3.14

    def __init__(self, radius):
        self.radius = radius

    @classmethod
    def from_diameter(cls, diameter):
        return cls(diameter / 2)

circle = Circle.from_diameter(10)
print(circle.radius)  # Output: 5

在上述示例中,Circle类定义了一个类方法from_diameter,用于根据给定直径创建一个圆对象。该类方法接受一个参数diameter,并使用它来计算半径,并通过cls(diameter / 2)创建一个新的Circle对象。在这里,cls表示类本身,可以在类方法内部使用它来引用类。

在主程序中,我们使用Circle.from_diameter(10)调用了类方法from_diameter,传递参数10作为直径。该类方法内部使用直径计算出半径,并通过cls(diameter / 2)创建了一个新的Circle对象。最终,我们打印出该对象的半径,输出结果为5。

类方法常用于创建替代构造函数或提供便捷的对象创建方式。它们可以在不直接实例化类的情况下创建对象,并提供更多的灵活性和可读性。

请注意,类方法的第一个参数约定为cls,表示类本身。通过这个参数,我们可以在类方法内部引用类的属性或调用其他类方法。与实例方法不同,类方法不会自动传递实例作为第一个参数。

8.4 python的装饰器和java的组件相似的地方

虽然 Python 的装饰器和 Java 中的某些组件并没有直接对应的概念或语言特性,但可以通过比较它们的功能和用途来找到一些相似之处。

(1)切面编程:Java 中的切面编程(Aspect-Oriented Programming,AOP)可以通过使用框架如 Spring AOP 来实现,在运行时动态地将一些额外的逻辑织入到方法调用中。这与 Python 装饰器的目的相似,通过在函数定义前添加装饰器来添加额外的逻辑,例如日志记录、性能监控等。

(2)注解:Java 中的注解(Annotation)是一种元数据,可以用于给类、方法或字段添加附加信息。类似地,在 Python 中,装饰器也可以看作是一种注解,通过在函数或类上使用装饰器来添加额外的功能或修改其行为。

(3)接口和适配器模式:在 Java 中,接口和适配器模式用于实现对象之间的解耦和灵活性。类似地,Python 装饰器可以用于实现类或函数之间的解耦,将额外的功能通过装饰器应用到目标对象上。

虽然 Python 的装饰器在语法和实现上与 Java 中的某些组件不同,但它们都提供了一种灵活的机制来修改代码行为、实现横切关注点和增加额外功能。

8.5 列举装饰器五个场景的示例

(1)添加日志记录的装饰器:

def log_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"Calling function: {func.__name__}")
        result = func(*args, **kwargs)
        print(f"Function {func.__name__} executed")
        return result
    return wrapper

@log_decorator
def add(a, b):
    return a + b

print(add(2, 3))

(2)计时函数执行时间的装饰器:

import time

def timer_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        execution_time = end_time - start_time
        print(f"Function {func.__name__} executed in {execution_time} seconds")
        return result
    return wrapper

@timer_decorator
def multiply(a, b):
    time.sleep(2)
    return a * b

print(multiply(3, 4))

(3)缓存函数结果的装饰器:

def cache_decorator(func):
    cache = {}

    def wrapper(*args, **kwargs):
        key = tuple(args) + tuple(sorted(kwargs.items()))
        if key in cache:
            print(f"Returning cached result for function: {func.__name__}")
            return cache[key]
        result = func(*args, **kwargs)
        cache[key] = result
        return result

    return wrapper

@cache_decorator
def factorial(n):
    if n == 0 or n == 1:
        return 1
    return n * factorial(n - 1)

print(factorial(5))
print(factorial(5))

(4)验证权限的装饰器:

def permission_decorator(permission):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if has_permission(permission):
                return func(*args, **kwargs)
            else:
                raise PermissionError("Access denied")
        return wrapper
    return decorator

def has_permission(permission):
    # Check if user has the required permission
    return True

@permission_decorator("admin")
def delete_user(user_id):
    # Delete user
    print(f"Deleted user with ID: {user_id}")

delete_user(123)

(5)修改函数行为的装饰器:

def modify_behavior_decorator(func):
    def wrapper(*args, **kwargs):
        # Modify behavior before calling the function
        print("Before function execution")
        result = func(*args, **kwargs)
        # Modify behavior after function execution
        print("After function execution")
        return result
    return wrapper

@modify_behavior_decorator
def greet(name):
    print(f"Hello, {name}")

greet("Alice")

以上是针对添加日志记录、计时函数执行时间、缓存函数结果、验证权限和修改函数行为等五个场景的装饰器示例代码。每个装饰器都通过定义一个包装函数(wrapper)来包裹原始函数,并在需要的地方添加额外的逻辑。通过将装饰器应用到目标函数上,可以实现对函数行为的修改或扩展。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值