page contents

Python教程:python闭包与装饰器!

Python 中的闭包(Closure)和装饰器(Decorator)是非常重要的概念,它们在函数式编程中扮演着关键角色。闭包允许我们封装状态,而装饰器则提供了一种动态修改函数行为的方式。下面我们将详细介绍这两个概念,并通过多个示例来展示它们的用法。

attachments-2024-09-MPGTHp6E66da5aa0ea763.jpg

Python 中的闭包(Closure)和装饰器(Decorator)是非常重要的概念,它们在函数式编程中扮演着关键角色。闭包允许我们封装状态,而装饰器则提供了一种动态修改函数行为的方式。下面我们将详细介绍这两个概念,并通过多个示例来展示它们的用法。

闭包

闭包是一种函数,它可以访问并操作在其外部定义的变量。这种能力使得闭包能够记住并访问其创建时所在作用域内的变量。

简单的闭包

def outer_function(x):    def inner_function(y):        return x + y    return inner_functionclosure = outer_function(10)print(closure(5))  # 输出: 15

在这个例子中,outer_function 接受一个参数 x 并返回一个内部函数 inner_function。inner_function 访问并使用了 outer_function 中定义的变量 x。


使用闭包来创建计数器

def create_counter():    count = 0    def increment():        nonlocal count        count += 1        return count    return incrementcounter = create_counter()print(counter())  # 输出: 1print(counter())  # 输出: 2print(counter())  # 输出: 3

在这个例子中,create_counter 函数返回一个 increment 函数,该函数每次调用时都会递增 count 变量的值。

装饰器

装饰器是一种特殊的闭包,它接收一个函数作为参数,并返回一个新的函数。装饰器通常用于增强或修改已有的函数功能,而无需修改函数本身的源代码。

简单的装饰器

def simple_decorator(func):    def wrapper():        print("Before the function is called.")        func()        print("After the function is called.")    return wrapper@simple_decoratordef say_hello():    print("Hello!")say_hello()# 输出:# Before the function is called.# Hello!# After the function is called.在这个例子中,@simple_decorator 语法糖等价于 say_hello = simple_decorator(say_hello)。

装饰器 simple_decorator 在调用 say_hello 之前和之后分别打印了一些信息。


带参数的装饰器

def repeat(n_times):    def decorator(func):        def wrapper(*args, **kwargs):            for _ in range(n_times):                result = func(*args, **kwargs)            return result        return wrapper    return decorator@repeat(n_times=3)def greet(name):    print(f"Hello {name}")greet("Alice")# 输出:# Hello Alice# Hello Alice# Hello Alice在这个例子中,repeat 装饰器本身接受一个参数 n_times,然后返回一个真正的装饰器函数 decorator。

这个装饰器函数再接受一个函数 func 作为参数,并返回一个包装函数 wrapper。


装饰器带参数和关键字参数

def log(function=None, *, prefix=""):    def decorator(func):        def wrapper(*args, **kwargs):            print(prefix + "Calling function", func.__name__)            result = func(*args, **kwargs)            print(prefix + "Function finished")            return result        return wrapper    if function is not None:        return decorator(function)    return decorator@log(prefix=">>> ")def add(a, b):    return a + bprint(add(1, 2))# 输出:# >>> Calling function add# >>> Function finished# 3

在这个例子中,log 装饰器可以接受一个可选的 prefix 参数。如果装饰器直接被调用(没有参数),则它返回一个包装函数;如果有参数,则返回一个真正的装饰器函数。


使用闭包保存状态

def cache(maxsize=128):    cache_dict = {}    def decorator(func):        def wrapper(*args):            if args in cache_dict:                print(f"Returning cached result for {args}")                return cache_dict[args]            else:                if len(cache_dict) >= maxsize:                    cache_dict.popitem(last=False)                result = func(*args)                cache_dict[args] = result                return result        return wrapper    return decorator@cache(maxsize=10)def fibonacci(n):    if n <= 1:        return n    else:        return fibonacci(n-1) + fibonacci(n-2)print(fibonacci(10))# 输出:# Returning cached result for (10,)# 55

在这个例子中,cache 装饰器创建了一个缓存字典 cache_dict 来存储函数 fibonacci 的结果。当再次调用相同参数的 fibonacci 时,装饰器会返回缓存的结果。

进阶闭包示例

创建带状态的日志记录器

def create_logger(log_level):    logs = []    def logger(message):        nonlocal logs        logs.append((log_level, message))        print(f"[{log_level}] {message}")    def get_logs():        return logs    return logger, get_logsinfo_logger, info_get_logs = create_logger("INFO")error_logger, error_get_logs = create_logger("ERROR")info_logger("This is an info message.")error_logger("This is an error message.")print(info_get_logs())  # 输出: [('INFO', 'This is an info message.')]print(error_get_logs())  # 输出: [('ERROR', 'This is an error message.')]在这个示例中,create_logger 函数返回两个闭包:logger 和 get_logs。

logger 用于记录日志信息,get_logs 用于获取所有记录的日志。


动态创建类

动态创建类

def create_class_with_property(property_name):    class DynamicClass:        def __init__(self):            self._property = None        @property        def prop(self):            return getattr(self, f"_{property_name}")        @prop.setter        def prop(self, value):            setattr(self, f"_{property_name}", value)    return DynamicClassDynamicClassWithProperty = create_class_with_property("value")instance = DynamicClassWithProperty()instance.prop = 10print(instance.prop)  # 输出:10

在这个示例中,create_class_with_property 函数动态创建了一个类,并给这个类添加了一个带有 getter 和 setter 的属性。

进阶装饰器示例

装饰器链

def debug(func):

    def wrapper(*args, **kwargs):

        print(f"Calling {func.__name__}")

        result = func(*args, **kwargs)

        print(f"Result: {result}")

        return result

    return wrapper

def retry(times=3):

    def decorator(func):

        def wrapper(*args, **kwargs):

            for _ in range(times):                try:                    return func(*args, **kwargs)                except Exception as e:                    print(f"Error: {e}")            raise Exception("Retry limit exceeded")        return wrapper    return decorator@debug@retry(times=2)def risky_operation():    # 模拟可能出错的操作    import random    if random.choice([True, False]):        return "Success"    else:        raise ValueError("Operation failed")print(risky_operation())

在这个示例中,我们使用了装饰器链,@debug 和 @retry 一起工作,使函数具备调试和重试的功能。


参数化装饰器

def parametrized_decorator(param):    def decorator(func):        def wrapper(*args, **kwargs):            print(f"Parameter: {param}")            return func(*args, **kwargs)        return wrapper    return decorator@parametrized_decorator("hello")def greet(name):    return f"Hello, {name}"print(greet("Alice"))# 输出:# Parameter: hello# Hello, Alice

这个示例展示了如何创建一个参数化的装饰器。

使用闭包来维护函数签名
from functools import wrapsdef trace(func):    @wraps(func)    def wrapper(*args, **kwargs):        print(f"Calling {func.__name__}")        result = func(*args, **kwargs)        print(f"Result: {result}")        return result    return wrapper@tracedef add(a, b):    """Add two numbers"""    return a + bprint(add(1, 2))# 输出:# Calling add# Result: 3# 3print(add.__doc__)  # 输出: Add two numbers在这个示例中,我们使用了 functools.wraps 来保留原始函数的元信息,如名称和文档字符串。

多线程装饰器
import threadingfrom functools import wrapsdef thread_safe(func):    lock = threading.Lock()    @wraps(func)    def wrapper(*args, **kwargs):        with lock:            return func(*args, **kwargs)    return wrapper@thread_safedef safe_increment(counter):    counter[0] += 1    return counter[0]counter = [0]print(safe_increment(counter))  # 输出: 1print(safe_increment(counter))  # 输出: 2在这个示例中,thread_safe 装饰器使用了一个锁来确保函数在多线程环境下的安全执行。

装饰器工厂
def memoize(maxsize=128):    cache = {}    def decorator(func):        @wraps(func)        def wrapper(*args):            if args in cache:                return cache[args]            result = func(*args)            if len(cache) >= maxsize:                cache.popitem(last=False)            cache[args] = result            return result        return wrapper    return decorator@memoize(maxsize=10)def fibonacci(n):    if n <= 1:        return n    else:        return fibonacci(n-1) + fibonacci(n-2)print(fibonacci(10))  # 输出: 55这个示例展示了如何创建一个装饰器工厂,该工厂可以根据传入的参数创建不同的装饰器实例。
总结
闭包和装饰器是 Python 中非常强大的工具,可以用于封装状态、修改函数行为、增强功能等。

更多相关技术内容咨询欢迎前往并持续关注好学星城论坛了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

  • 发表于 2024-09-06 09:28
  • 阅读 ( 54 )
  • 分类:Python开发

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
小柒
小柒

1470 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1470 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章